-
Notifications
You must be signed in to change notification settings - Fork 7
/
chap19.xml
1004 lines (859 loc) · 48.2 KB
/
chap19.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"
[
<!ENTITY BASEID "djangobook.chap19">
]>
<chapter lang="ru" id="&BASEID;">
<title id="&BASEID;.title">
Безопасность
</title>
<para>
Данная глава временно взята из первой версии книги и подлежит
корректировке. Вы можете помочь с этим!
</para>
<para>
Перевод © Попов Руслан <radz • yandex • ru>
</para>
<para>
Интернет может быть страшным местом.
</para>
<para>
В наши дни, high-profile security gaffes seem to crop up on a
daily basis. Мы видели вирусы, распространяющиеся с огромной
скоростью, рои взломанных компьютеров становились оружием,
бесконечная гонка вооружений в войне со спаммерами, и множество
сообщений о взломанных веб сайтах.
</para>
<para>
Будучи веб разработчиками, мы имеем обязанность делать всё
необходимое для противодействия этим силам тьмы. Каждый веб
разработчик должен рассматривать безопасность в качестве
фундаментального аспекта своей работы. К сожалению, реализовать
безопасную среду <emphasis>сложно</emphasis> — хакерам
достаточно найти единственную уязвимость, а защитникам надо
закрыть каждую из них.
</para>
<para>
Django пытается смягчить эту проблему. Оно спроектировано для
автоматической защиты вашего проекта от большинства стандартных
ошибок программирования, влияющих на безопасность, которые часто
делают новые (и даже опытные) разработчики. Важно понимать в чём
заключаются эти ошибки, как Django может защитить вам и, самое
главное, что вы можете сделать для улучшения безопасности вашего
кода.
</para>
<para>
Несмотря на всё это, сначала сделаем важное заявление: Мы не
пытаемся предоставить полное руководство для каждого известного
эксплоита FIXME и, соответственно, мы не будем пытаться объяснить
каждую уязвимость подробно. Вместо этого, мы будет предоставлять
краткое описание проблем на примере Django.
</para>
<section id="&BASEID;.the-theme">
<title id="&BASEID;.the-theme.title">
Тема безопасности в вебе
</title>
<para>
Если вы вынесите только одну вещь из этой главы, то пусть это
будет — <quote>Никогда, ни при каких условиях, не
доверяйте никаким данным полученным от пользователей</quote>.
</para>
<para>
Вы <emphasis>никогда</emphasis> не будете знать, кто находится
на другой стороне HTTP соединения. Это может быть один из ваших
пользователей, но так же легко это может быть гнусный хакер,
ищущий дыру в вашем сайте.
</para>
<para>
Любые данных из любого формата, которые приходит от
пользовательских браузеров, должны рассматриваться с достаточным
объёмом паранойи. Под этими данными понимаются как
<quote>внутренние</quote> (т.е., переданные через формы), так и
<quote>внешние</quote> (т.е., HTTP заголовки, cookie и другая
информация HTTP запроса). Нет никаких сложностей в подмене
метаинформации запроса, которую браузеры автоматически добавляют
к нему.
</para>
<para>
Каждая из описываемых в данной главе уязвимостей происходит
напрямую из-за доверия к данным, которые пришли по проводам и
которые не были проверены перед их использованием. Вы должны
постоянно спрашивать себя: <quote>Откуда пришли эти
данные?</quote>
</para>
</section>
<section id="&BASEID;.sql-injection">
<title id="&BASEID;.sql-injection.title">
Внедрение SQL
</title>
<para>
<emphasis>SQL инъекция</emphasis> — это стандартная
уязвимость, в которой атакующая сторона изменяет параметры веб
страницы (такие как GET/POST данный или URL) для внедрения
определённых кусков SQL запроса, которые наивное веб приложение
выполняет напрямую над своей базой данных. Вероятно, это
наиболее опасная и, к сожалению, наиболее часто встречающаяся
уязвимость.
</para>
<para>
Данная уязвимость чаще всего проявляется, когда SQL
<emphasis>собирается</emphasis> вручную на основе
пользовательского ввода. Например, представим создание функции
для получения списка с контактной информацией с помощью страницы
поиска. Для защиты данных об адресах электронной почты от
спаммеров, мы будем требовать от пользователя ввода чьего-нибудь
имени и будет предоставлять адрес для введённого имени:
<screen>
<![CDATA[
def user_contacts(request):
user = request.GET['username']
sql = "SELECT * FROM user_contacts WHERE username = '%s';" % username
# execute the SQL here...
]]>
</screen>
<note>
<para>
В данном примере, и в других подобных <quote><emphasis>не
делай так</emphasis></quote> примерах, мы намеренно опустим
большую часть кода, необходимого для работоспособности
описываемых функций. Мы не желаем, чтобы кто-нибудь
использовал такой код вне контекста данной главы.
</para>
</note>
</para>
<para>
И хотя такой код не выглядит опасным, он таковым является.
</para>
<para>
Во-первых, наша попытка защитить весь наш список адресов
электронной почты провалится из-за хитро составленного
запроса. Подумайте о том, что может случиться, если атакующий
введёт <token>' OR 'a'='a</token> в поле запроса. В этом случае,
будет сконструирован следующий запрос:
<screen>
<![CDATA[
SELECT * FROM user_contacts WHERE username = '' OR 'a' = 'a';
]]>
</screen>
</para>
<para>
Так как мы не проверяем введённую строку на наличие SQL команд,
атакующий добавил выражение <token>OR</token> и это приведёт к
выдаче всех записей из соответствующей таблицы.
</para>
<para>
Однако, существует <emphasis>как минимум ещё одна</emphasis>
опасность. Представьте, что может случиться, если атакующий
введёт <token>'; DELETE FROM user_contacts WHERE
'a'='a'</token>. Вот так будет выглядеть сконструированный
запрос:
<screen>
<![CDATA[
SELECT * FROM user_contacts WHERE username = '';
DELETE FROM user_contacts WHERE 'a' = 'a';
]]>
</screen>
</para>
<para>
Ой! А куда делся наш список контактов?
</para>
<section id="&BASEID;.sql-injection.solution">
<title id="&BASEID;.sql-injection.solution.title">
Решение
</title>
<para>
Несмотря на то, что данная проблема является неочевидной,
решение для неё будет простым: <emphasis>никогда</emphasis> не
доверяйте пользовательским данным и
<emphasis>всегда</emphasis> экранируйте всё, что вы
используете при конструировании SQL запроса.
</para>
<para>
Всё это Django делает на уровне API для работы с базой
данных. Он автоматически экранирует все специальные SQL
параметры, учитывая соглашение об использовании кавычек для
используемого вами сервера баз данных (т.е., PostgreSQL или
MySQL).
</para>
<para>
Например, в данном вызове API:
<screen>
<![CDATA[
foo.get_list(bar__exact="' OR 1=1")
]]>
</screen>
Django выполнит соответствующее экранирование и результирующий
оператор будет выглядеть так:
<screen>
<![CDATA[
SELECT * FROM foos WHERE bar = '\' OR 1=1'
]]>
</screen>
т.е., очень безобидно.
</para>
<para>
Это поведение характерно для всего API с некоторыми
исключениями:
<itemizedlist>
<listitem>
<para>
Аргумент <token>where</token> у метода
<function>extra()</function> (см. приложение
<quote><xref linkend="djangobook.appendix_c"
endterm="djangobook.appendix_c.title"/></quote>). Этот
параметр принимает <emphasis>сырой</emphasis> SQL, так
было заложено при его разработке).
</para>
</listitem>
<listitem>
<para>
Вручную созданные запросы с помощью API низкого уровня.
</para>
</listitem>
</itemizedlist>
</para>
<para>
В каждом из этих случаев, несложно защитить себя от
ошибок. Надо просто использовать <emphasis>привязку
переменных</emphasis> вместо конструирования запросов. Таким
образом, предыдущий пример должен быть переписан так:
<screen>
<![CDATA[
from django.db import connection
def user_contacts(request):
user = request.GET['username']
sql = "SELECT * FROM user_contacts WHERE username = %s;"
cursor = connection.cursor()
cursor.execute(sql, [user])
# ... do something with the results
]]>
</screen>
</para>
<para>
Низкоуровневый метод <function>execute()</function> принимает
с SQL строку с символами подстановки (<token>%s</token>) и
автоматически экранирует и подставляет параметры из списка,
переданного вторым аргументом. Вы должны
<emphasis>всегда</emphasis> конструировать свои SQL запросы
таким способом.
</para>
<para>
К сожалению, вы не можете свободно использовать привязку
переменных. Например, нельзя таким способом определять
идентификаторы (т.е., имя таблицы или полей
таблиц). Следовательно, если вам потребуется динамически
создать список таблиц по, скажем, содержимому переменной
<varname>POST</varname>, вам придётся экранировать их имена в
своём коде. Django предоставляет функцию,
<token>django.db.backend.</token><function>quote_name()</function>,
которая осуществляет экранирование идентификатора в
соответствии с принятой схемой квотирования для текущей базы
данных.
</para>
</section>
</section>
<section id="&BASEID;.xss">
<title id="&BASEID;.xss.title">
Межсайтовый скриптинг (XSS)
</title>
<para>
Данный тип уязвимости проявляется в веб приложениях, которые не
производят экранирование информации, введённой пользователем,
перед её помещением в HTML страницы. Это позволяет атакующему
вставить определённый HTML код на вашу страницу, обычно это теги
<![CDATA[<script>]]>.
</para>
<para>
Злобные хакеры часто используют XSS для кражи cookie и
информации о сессии или вводят пользователей в заблуждение,
вынуждая их вводить важную информацию (так называемый
<emphasis>фишинг</emphasis> — рыбалка).
</para>
<para>
Данный тип атак может принимать различные формы и бесконечно
изменяться, так что мы рассмотрим типичный случай. Рассмотрим
чрезвычайно простое представление:
<screen>
<![CDATA[
def say_hello(request):
name = request.GET.get('name', 'world')
return render_to_response("hello.html", {"name" : name})
]]>
</screen>
</para>
<para>
Это представление просто получает имя из параметра
<token>GET</token> и передаёт его в шаблон
<filename>hello.html</filename>, который может выглядеть так:
<screen>
<![CDATA[
<h1>Hello, {{ name }}!</h1>
]]>
</screen>
</para>
<para>
Таким образом, если мы перейдём по URL
<token>http://example.com/hello/name=Jacob</token>, то
результатом будет страница, которая будет содержать:
<screen>
<![CDATA[
<h1>Hello, Jacob!</h1>
]]>
</screen>
</para>
<para>
Подождите, а что произойдёт, если перейти на
<token><![CDATA[http://example.com/hello/name=<i>Jacob</i>]]></token>?
Тогда мы получим это:
<screen>
<![CDATA[
<h1>Hello, <i>Jacob</i>!</h1>
]]>
</screen>
</para>
<para>
Конечно, хакеры не будут играть с тегами
<token><![CDATA[<i>]]></token>, они могут включить набор HTML
кодов, которые наполнят вашу страницу определённым
содержимым. Этот тип атак используется для принуждения
пользователей выдать, например, информацию о своём банковском
счёте, которая затем отправляется атакующему.
</para>
<para>
Проблема может стать ещё серьёзнее в случае, если вы сохраняете
эти данные в базе данных и затем отображаете их на своём
сайте. Например, на MySpace однажды нашли уязвимость, которая
позволила провести атаку такого типа. Один пользователь вставил
в свой профиль JavaScript, который автоматически добавлял этого
пользователя в качестве друга, если кто-то посещал его страницу
с профилем. За несколько дней у него появились миллионы друзей.
</para>
<para>
Это может казаться несерьёзным, но следует помнить, что
атакующий запустил <emphasis>свой</emphasis> код, не код
MySpace, на <emphasis>вашем</emphasis> компьютере. Это нарушает
предположение о том, что весь код на сайте MySpace действительно
написан этой компанией.
</para>
<para>
Компании MySpace очень повезло, что этот код не принялся
автоматически удалять пользовательские аккаунты, изменять их
пароли, заваливать сайт спамом или выполнять какой-нибудь ещё
кошмарный сценарий, который позволяет данная уязвимость.
</para>
<section id="&BASEID;.xss.solution">
<title id="&BASEID;.xss.solution.title">
Решение
</title>
<para>
Решение простое — <emphasis>всегда</emphasis>
экранируйте <emphasis>любую</emphasis> информацию с помощью
тега <token>escape</token> (или его эквивалента) при
отображение на вашем сайте информации, введённой
пользователем.
<note>
<title>
Почему Django не делает это автоматически?
</title>
<para>
<quote>Пусть Django автоматически экранирует все
переменные, отображаемые в шаблонах</quote> — эта
тема достаточно часто возникает в листе рассылки
разработчиков.
</para>
<para>
Пока шаблонная система Django избегает такого поведения,
потому что при этом бы незаметно изменилось бы то, что
должно работать прямолинейно — отображать
переменные. Это очень хитрая задача, чтобы оценить её
реализацию. К тому же, добавление скрытого неявного
поведения идёт против основных идеалов Django (да и Python
тоже), но обеспечение безопасности также является важной
задачей.
</para>
<para>
Всё, что мы хотим этим сказать, — возможно
когда-нибудь Django дорастёт до некоторой формы
автоматического (или почти автоматического) экранирования
в будущем. Рекомендуем периодически проверять официальную
документацию на предмет нововведений. Она всегда будет
содержать более актуальную информацию, чем эта книга.
</para>
<para>
Даже если эта функциональность будет добавлена в Django,
вы <emphasis>всё ещё</emphasis> будете должны иметь
привычку спрашивать себя каждый раз — <quote>Откуда
пришли эти данные?</quote>. Никакое автоматическое решение
не защитит ваш сайт от XSS атак на 100%.
</para>
</note>
</para>
</section>
</section>
<section id="&BASEID;.csrf">
<title id="&BASEID;.csrf.title">
Подделка HTTP запросов
</title>
<para>
Атаки типа <quote>подделка HTTP запросов</quote> (CSRF)
случаются когда вредоносный веб сайт принуждает пользователей
неявно загружать URL с сайта, на котором они были
аутентифицированы, а следовательно, пользуется их правами.
</para>
<para>
Djagno обладает встроенными инструментами для защиты от этого
типа атак. Сама атака и эти инструменты описаны в главе
<quote><xref linkend="djangobook.chap14"
endterm="djangobook.chap14.title"/></quote>.
</para>
</section>
<section id="&BASEID;.session">
<title id="&BASEID;.session.title">
Подделка сессий
</title>
<para>
Подделка сессий не является каким-то видом атаки, это целый
класс атак, нацеленных на данные пользовательской сессии. Они
могут принимать ряд различных форм:
<itemizedlist>
<listitem>
<para>
<emphasis>Человек посередине</emphasis> — атакующий
читает данные сессии в момент когда они проходят мимо него
по сети.
</para>
</listitem>
<listitem>
<para>
<emphasis>Подделка сессии</emphasis> — атакующий
использует идентификатор сессии (возможно полученный с
помощью атаки <quote>человек посередине</quote>) для того,
чтобы притвориться другим пользователем.
</para>
<para>
Примером этих двух форм будет ситуация когда атакующий,
сидя в кофейне, используя локальную WiFi сеть будет ловить
идентификаторы сессий. Затем эти идентификаторы будут
использоваться, чтобы представиться оригинальным
пользователем.
</para>
</listitem>
<listitem>
<para>
<emphasis>Подделка cookie</emphasis> — атакующий
переопределяет данные (они могут быть в режиме
<quote>только для чтения</quote>) в cookie. Глава
<quote><xref linkend="djangobook.chap12"
endterm="djangobook.chap12.title"/></quote> детально
описывает работу cookie, одной из ярких особенностей
которых является простота незаметного изменения
содержимого браузером или злонамеренными пользователями.
</para>
<para>
Существует длинная история о сайтах, которые хранили
cookie подобные <token>IsLoggedIn=1</token> или даже
<token>LoggedInAsUser=jacob</token>. Чрезвычайно просто
воспользоваться таким подарком.
</para>
<para>
Проще говоря, не стоит доверять данным, сохранённым в
cookie.
</para>
</listitem>
<listitem>
<para>
<emphasis>Фиксация сессии</emphasis> — атакующий
принуждает пользователя обновить идентификатор
пользовательской сессии.
</para>
<para>
Например, PHP позволяет передавать идентификаторы сессий
через URL (т.е.,
<token>http://example.com/?PHPSESSID=fa90197ca25f6ab40bb1374c510d7a32</token>). Атакующий
просто подставляет такой идентификатор в URL ссылки, на
которую нажмёт пользователь, заставляя последнего
подключиться к указанной сессии.
</para>
<para>
Фиксация сессии используется при <quote>рыбалке</quote>,
принуждая пользователя вводить свою персональную
информацию в аккаунт, которым владеет атакующий. Последний
может просмотреть свой аккаунт и получить введённые
данные.
</para>
</listitem>
<listitem>
<para>
<emphasis>Отравление сессии FIXME</emphasis> —
атакующий внедряет в пользовательскую сессию потенциально
опасные данные. Обычно это происходит при отправке форм,
которые использует пользователь для установки данных
сессии.
</para>
<para>
Каноническим примером будет сайт, который сохраняет
простые пользовательские настройки (подобные цвету фона
страницы) в cookie. Атакующий может подвести пользователя
к нажатию ссылки для отправки <quote>цвета</quote>,
который на самом деле содержит данные для XSS атаки. Если
переданные данные не экранируются на стороне сервера,
пользователь может снова внедрить вредоносный код в свою
среду.
</para>
</listitem>
</itemizedlist>
</para>
<section id="&BASEID;.session.solution">
<title id="&BASEID;.session.solution.title">
Решение
</title>
<para>
Существует целый ряд основных принципов, следование которым
может защитить вас от этих атак:
<itemizedlist>
<listitem>
<para>
Никогда не позволяйте информации о сессии быть в URL.
</para>
<para>
Среда Django для работы с сессиями, описанная в главе
<quote><xref linkend="djangobook.chap12.session"
endterm="djangobook.chap12.session.title"/></quote>,
просто не позволяет так делать.
</para>
</listitem>
<listitem>
<para>
Не используйте данные в cookie напрямую. Вместо этого,
храните идентификатор сессии, которая в свою очередь
хранится в базе данных (например).
</para>
<para>
Если вы используете встроенную среду для работы с
сессиями (т.е., <token>request.session</token>), это
будет выполняться автоматически. Среда использует
единственный cookie, в котором хранится единственный
идентификатор сессии, а данные самой сессии хранятся в
базе данных.
</para>
</listitem>
<listitem>
<para>
Не забывайте экранировать данные сессии, если вы
отображаете их в шаблоне. Обратитесь к секции
<quote><xref linkend="&BASEID;.xss"
endterm="&BASEID;.xss.title"/></quote> и запомните, что
он применяется к любой информации, которую создал
пользователь, а также к любой информации, полученной от
браузера. Вы должны рассматривать информацию из сессии,
как информацию полученную от пользователя.
</para>
</listitem>
<listitem>
<para>
Не позволяйте атакующим перехватывать идентификаторы
сессий.
</para>
<para>
Несмотря на то, что практически невозможно определить
ситуацию, когда кто-то похитил идентификатор сессии,
Django обладает встроенным механизмом защиты от атак
прямого перебора идентификаторов сессии. Идентификатор
сессии хранится в виде хэша (а не в виде
последовательного числа), это затрудняет прямой перебор,
а пользователь всегда получает новый идентификатор
сессии, как только пытается использовать несуществующий
идентификатор, что помогает избежать фиксации сессии.
</para>
</listitem>
</itemizedlist>
</para>
<para>
Следует отметить, что ни один из этих принципов не защитит вас
от атаки <quote>человек посередине</quote>. Этот тип атак
практически невозможно распознать. Если ваш сайт позволяет
авторизованным пользователям просматривать важные данные, вы
должны <emphasis>всегда</emphasis> передавать их с помощью
протокола HTTPS. Также, если ваш сайт использует SSL, вы
должны назначить <token>True</token> значению
<varname>SESSION_COOKIE_SECURE</varname> — это заставит
Django отправлять cookie с идентификатор сессии по протоколу
HTTPS.
</para>
</section>
</section>
<section id="&BASEID;.email">
<title id="&BASEID;.email.title">
Внедрение E-mail заголовка
</title>
<para>
Внедрение SQL является менее известным аналогом
<emphasis>внедрения Email заголовка</emphasis>, которое крадёт
данные форм при их отправке на электронную почту. Атакующий
может использовать эту методику для рассылки спама через ваш
почтовый сервер. Любая форма, которая создаёт почтовые
заголовки, используя введённые в форму данные, уязвима для
данного типа атак.
</para>
<para>
Давайте рассмотрим стандартную контактную форму, которую можно
встретить на многих сайтах. Обычно, такая форма отсылает
сообщение на жёстко определённый адрес и, следовательно, не
подвержена уязвимости, через которую можно рассылать спам. Но
это только на первый взгляд.
</para>
<para>
Тем не менее, большинство таких форм также позволяют
пользователю указать собственную тему для почтового сообщения (а
также свой адрес, текст сообщения, иногда и что-нибудь
ещё). Поле темы используется для генерации заголовка Subject для
почтового сообщения.
</para>
<para>
Если этот заголовок не экранируется во время генерации почтового
сообщения, атакующий может отправить что-нибудь вида
<token>hello\ncc:[email protected]</token> (где
<token>\n</token> является символом перевода строки). Это может
привести к созданию следующих заголовков:
<screen>
<![CDATA[
Subject: hello
]]>
</screen>
</para>
<para>
Аналогично внедрению SQL, если мы доверяем пользователю вводить
строку для темы письма, мы позволяем ему создавать вредоносный
набор заголовков и использовать нашу контактную форму для
рассылки спама.
</para>
<section id="&BASEID;.email.solution">
<title id="&BASEID;.email.solution.title">
Решение
</title>
<para>
Мы можем предотвратить такую атаку также как мы предотвращаем
внедрение SQL — всегда экранируйте или проверяйте
данные, которые вводит пользователь.
</para>
<para>
Встроенные в Django функции отправки электронной почты
(<token>django.core.mail</token>) просто не позволяют
использовать символы перевода строки в любом из полей,
используемых для создания заголовков (<token>from</token>,
<token>to</token> и <token>subject</token>). Если вы
попытаетесь использовать
<token>django.core.mail.</token><function>send_mail()</function>
совместно с полем <token>subject</token>, которое содержит
символ перевода строки, то Django вызовет исключение
<token>BadHeaderError</token>.
</para>
<para>
Если вы не используете встроенные в Django функции для
отправки электронной почты, вам потребуется проверять
заголовки на наличие там символа перевода строки. При их
наличии надо будет выдавать ошибку или тихо удалять их
оттуда. Рекомендуем посмотреть на класс
<classname>SafeMIMEText</classname> в
<token>django.core.mail</token>.
</para>
</section>
</section>
<section id="&BASEID;.traversal">
<title id="&BASEID;.traversal.title">
Directory Traversal
</title>
<para>
<emphasis>Directory Traversal FIXME</emphasis> является другим
типом атак на внедрение своей информации. В этом случае
злонамеренный пользователь принуждает систему прочитать и/или
записать файлы, доступа к которым веб сервер иметь не должен.
</para>
<para>
Примером может быть представление, которое читает файлы с диска
без аккуратной проверки их имени:
<screen>
<![CDATA[
def dump_file(request):
filename = request.GET["filename"]
filename = os.path.join(BASE_PATH, filename)
content = open(filename).read()
# ...
]]>
</screen>
</para>
<para>
Несмотря на то, что данное представление выглядит надёжным, так
как доступ к файлу ограничен <varname>BASE_PATH</varname> и
использованием
<token>os.path.</token><function>join()</function>, если
атакующий использует в имени файла символы <token>..</token>
(переход в родительский каталог), он может получить доступ к
файлам вне <varname>BASE_PATH</varname>. Имеет значение только
время, которое придётся затратить на поиск правильного числа
таких команд, скажем так:
<filename>../../../../../etc/passwd</filename>.
</para>
<para>
Любое представление, которое читает файлы без соответствующего
экранирования их имён уязвимы для этого типа
атак. Представления, которые <emphasis>пишут</emphasis> в файлы
также уязвимы, но вот только последствия страшнее.
</para>
<para>
Другой вариант FIXME??? этой проблемы находится в коде, который
динамически подгружает модули, основываясь на URL или другой
информации из запроса. Широко освещённый пример пришёл из мира
Ruby on Rails. До середины 2006 года Rails напрямую использовал
URL, подобные <token>http://example.com/person/poke/1</token>,
для загрузки модулей и вызова методов. В результате получалось
так, что специально созданный URL мог автоматически загружать
определённый код, включая скрипт для очистки базы данных!
</para>
<section id="&BASEID;.traversal.solution">
<title id="&BASEID;.traversal.solution.title">
Решение
</title>
<para>
Если ваш код действительно должен читать или записывать файлы,
основываясь на вводе пользователя, вам потребуется проверять
запрашиваемый путь очень аккуратно, чтобы удостовериться, что
атакующий не имеет возможности выйти из базового каталога, к
которому вы разрешили доступ.
<note>
<para>
Даже не стоит говорить о том, что
<emphasis>нельзя</emphasis> создавать код, который может
читать файлы из любого места на диске.
</para>
</note>
</para>
<para>
Хорошим примером экранирования можно считать представления из
<token>django.views.static</token>. Вот его примерный код:
<screen>
<![CDATA[
import os
import posixpath
# ...
path = posixpath.normpath(urllib.unquote(path))
newpath = ''
for part in path.split('/'):
if not part:
# strip empty path components
continue
drive, part = os.path.splitdrive(part)
head, part = os.path.split(part)
if part in (os.curdir, os.pardir):
# strip '.' and '..' in path
continue
newpath = os.path.join(newpath, part).replace('\\', '/')
]]>
</screen>
</para>
<para>
Django не читает файлы (пока вы не начнёте использовать
функцию <token>static.</token><function>serve()</function>),
таким образом данная уязвимость не влияет на код Django.
</para>
<para>
В дополнение скажем, что использование схемы URL означает, что
Django <emphasis>никогда</emphasis> не загружает код, пока вы
это явно не укажете. Не существует метода создать URL, который
заставит Django загрузить что-то не указанное в схеме URL.
</para>
</section>
</section>
<section id="&BASEID;.error-messages">
<title id="&BASEID;.error-messages.title">
Открытые сообщения об ошибках
</title>
<para>
Во время разработки наличие возможности просматривать
трассировочную информацию и ошибки в браузере является
чрезвычайно полезной. Django предоставляет
<quote>красивые</quote> и информативные отладочные сообщения.
</para>
<para>
Однако, если такие сообщения будут отображаться после
развёртывания сайта, они могут раскрыть аспекты работы вашего
кода или конфигурации сайта, которые могут помочь хакерам в их
нелёгкой работе.
</para>
<para>
Кроме того, сообщения об ошибках и трассировочная информация
бесполезна для конечных пользователей. Философия Django состоит
в том, что пользователи сайта никогда не должны видеть сообщения
об ошибках, относящиеся к приложению. Если ваш код вызывает
необработанное исключение, посетители сайта не должны увидеть
трассировочную информацию, вообще ничего. Вместо этого
посетители должны получить дружественное сообщение
<quote>Страница недоступна</quote>.
</para>
<para>
В действительности, разработчикам необходимо видеть отладочную
информацию для отладки ошибок в их коде. Таким образом, среда
разработки должна прятать все сообщения об ошибках от
пользователей, но должна отображать их доверяемым разработчикам
сайта.
</para>
<section id="&BASEID;.error-messages.solution">
<title id="&BASEID;.error-messages.solution.title">
Решение
</title>
<para>
Django использует простой флаг, который управляет отображением
сообщений об ошибках. Если параметр <varname>DEBUG</varname>
установлен в <token>True</token>, то сообщения об ошибках
будут отображаться в браузере. В противном случае, Django
отобразит сообщение с ошибкой 500 (<quote>Internal server
error</quote>), используя предоставленный вами для этого
случая шаблон. Этот шаблон должен находиться в файле
<filename>500.html</filename> и располагаться в корне одного
из ваших шаблонных каталогов.
</para>
<para>
Однако разработчикам потребуется получать сообщения об ошибках
от развёрнутого на боевом сервере сайта. Для этого каждая
ошибка будет отправляться с приложением полной трассировочной
информации на почтовые адреса, указанные в параметре
<varname>ADMINS</varname>.
</para>
<para>
При использовании Apache в качестве веб сервера и
<token>mod_python</token> необходимо проверить наличие
следующей строки в файле конфигурации Apache:
<screen>
<![CDATA[
PythonDebug Off
]]>
</screen>
эта строка подавляет сообщения обо всех ошибках, которые
возникают до передачи управления Django.
</para>
</section>
</section>
<section id="&BASEID;.final-word">
<title id="&BASEID;.final-word.title">
Заключительное слово о безопасности
</title>
<para>
Мы надеемся, что всё рассказанное здесь не слишком испугало
вас. Интернет может быть диким и опасным миром, но немного
предусмотрительности может вам помочь.
</para>
<para>
Просто запомните, что безопасность в Интернет является постоянно
изменяющейся темой. Если вы читаете старую версию этой книги,
вам следует обратить внимание на ресурсы в сети,
специализирующиеся на защите информации, на предмет наличия
любых новых уязвимостей. Будет хорошей идеей тратить некоторое
время каждую неделю или месяц на изучение новостей в области
безопасности. Эта небольшая инвестиция времени может бесценно
помочь вашему сайту и вашим пользователям.
</para>