The Django Book

Chapter 19: Security

绗崄涔濈珷 瀹夊叏

The Internet can be a scary place.

Internet骞朵笉瀹夊叏銆

These days, high-profile security gaffes seem to crop up on a daily basis. Weve seen viruses spread with amazing speed, swarms of compromised computers wielded as weapons, a never-ending arms race against spammers, and many, many reports of identify theft from hacked Web sites.

鐜板浠婏紝姣忓ぉ閮戒細鍑虹幇鏂扮殑瀹夊叏闂銆傛垜浠洰鐫硅繃鐥呮瘨椋為熷湴钄撳欢锛屽ぇ閲忚鎺у埗鐨勮倝楦′綔涓烘鍣ㄦ潵鏀诲嚮鍏朵粬浜猴紝涓庡瀮鍦鹃偖浠剁殑姘告棤姝㈠鐨勫啗澶囩珵璧涳紝浠ュ強璁歌澶氬绔欑偣琚粦鐨勬姤鍛娿

As Web developers, we have a duty to do what we can to combat these forces of darkness. Every Web developer needs to treat security as a fundamental aspect of Web programming. Unfortunately, it turns out that implementing security is hard attackers need to find only a single vulnerability, but defenders have to protect every single one.

浣滀负web寮鍙戜汉鍛橈紝鎴戜滑鏈夎矗浠绘潵瀵规姉杩欎簺榛戞殫鐨勫姏閲忋傛瘡涓涓獁eb寮鍙戣呴兘搴旇鎶婂畨鍏ㄧ湅鎴愭槸web缂栫▼涓殑鍩虹閮ㄥ垎銆備笉骞哥殑鏄紝瑕佸疄鐜板畨鍏ㄦ槸鍥伴毦鐨勩傛敾鍑昏呭彧闇瑕佹壘鍒颁竴涓井灏忕殑钖勫急鐜妭锛岃岄槻瀹堟柟鍗磋淇濇姢寰楅潰闈勘鍒般

Django attempts to mitigate this difficulty. Its designed to automatically protect you from many of the common security mistakes that new (and even experienced) Web developers make. Still, its important to understand what these problems are, how Django protects you, and most important the steps you can take to make your code even more secure.

Django璇曞浘鍑忚交杩欑闅惧害銆傚畠琚璁′负鑷姩甯綘閬垮厤涓浜泈eb寮鍙戞柊鎵嬶紙鐢氳嚦鏄佹墜锛夌粡甯镐細鐘殑閿欒銆傚敖绠″姝わ紝闇瑕佸紕娓呮锛孌jango濡備綍淇濇姢鎴戜滑锛屼互鍙婃垜浠彲浠ラ噰鍙栧摢浜涢噸瑕佺殑鏂规硶鏉ヤ娇寰楁垜浠殑浠g爜鏇村姞瀹夊叏銆

First, though, an important disclaimer: We do not intend to present a definitive guide to every known Web security exploit, and so we wont try to explain each vulnerability in a comprehensive manner. Instead, well give a short synopsis of security problems as they apply to Django.

棣栧厛锛屼竴涓噸瑕佺殑鍓嶆彁锛氭垜浠苟涓嶆墦绠楃粰鍑簑eb瀹夊叏鐨勪竴涓灏界殑璇存槑锛屽洜姝ゆ垜浠篃涓嶄細璇︾粏鍦拌В閲婃瘡涓涓杽寮辩幆鑺傘傚湪杩欓噷锛屾垜浠細缁欏嚭Django鎵闈复鐨勫畨鍏ㄩ棶棰樼殑涓涓ぇ姒傘

The Theme of Web Security

Web瀹夊叏鐜扮姸

If you learn only one thing from this chapter, let it be this:

濡傛灉浣犱粠杩欑珷涓彧瀛﹀埌浜嗕竴浠朵簨鎯咃紝閭d箞瀹冧細鏄細

Never under any circumstances trust data from the browser.

鍦ㄤ换浣曟潯浠朵笅閮戒笉瑕佺浉淇℃祻瑙堝櫒绔彁浜ょ殑鏁版嵁銆

You never know whos on the other side of that HTTP connection. It might be one of your users, but it just as easily could be a nefarious cracker looking for an opening.

浣犱粠涓嶄細鐭ラ亾HTTP杩炴帴鐨勫彟涓绔細鏄皝銆傚彲鑳芥槸涓涓甯哥殑鐢ㄦ埛锛屼絾鏄悓鏍峰彲鑳芥槸涓涓鎵炬紡娲炵殑閭伓鐨勯獓瀹€

Any data of any nature that comes from the browser needs to be treated with a healthy dose of paranoia. This includes data thats both in band (i.e., submitted from Web forms) and out of band (i.e., HTTP headers, cookies, and other request information). Its trivial to spoof the request metadata that browsers usually add automatically.

浠庢祻瑙堝櫒浼犺繃鏉ョ殑浠讳綍鎬ц川鐨勬暟鎹紝閮介渶瑕佽繎涔庣媯鐑湴鎺ュ彈妫鏌ャ傝繖鍖呮嫭鐢ㄦ埛鏁版嵁锛堟瘮濡倃eb琛ㄥ崟鎻愪氦鐨勫唴瀹癸級鍜屽甫澶栨暟鎹紙姣斿锛孒TTP澶淬乧ookies浠ュ強鍏朵粬淇℃伅锛夈傝淇敼閭d簺娴忚鍣ㄨ嚜鍔ㄦ坊鍔犵殑鍏冩暟鎹紝鏄竴浠跺緢瀹规槗鐨勪簨銆

Every one of the vulnerabilities discussed in this chapter stems directly from trusting data that comes over the wire and then failing to sanitize that data before using it. You should make it a general practice to continuously ask, Where does this data come from?

鍦ㄨ繖涓绔犳墍鎻愬埌鐨勬墍鏈夌殑瀹夊叏闅愭偅閮界洿鎺ユ簮鑷浼犲叆鏁版嵁鐨勪俊浠伙紝骞朵笖鍦ㄤ娇鐢ㄥ墠涓嶅姞澶勭悊銆備綘闇瑕佷笉鏂湴闂嚜宸憋紝杩欎簺鏁版嵁浠庝綍鑰屾潵銆

SQL Injection

SQL娉ㄥ叆

SQL injection is a common exploit in which an attacker alters Web page parameters (such as GET /POST data or URLs) to insert arbitrary SQL snippets that a naive Web application executes in its database directly. Its probably the most dangerous and, unfortunately, one of the most common vulnerabilities out there.

SQL娉ㄥ叆 鏄竴涓緢甯歌鐨勫舰寮忥紝鍦⊿QL娉ㄥ叆涓紝鏀诲嚮鑰呮敼鍙榳eb缃戦〉鐨勫弬鏁帮紙渚嬪 GET /POST 鏁版嵁鎴栬匲RL鍦板潃锛夛紝鍔犲叆涓浜涘叾浠栫殑SQL鐗囨銆傛湭鍔犲鐞嗙殑缃戠珯浼氬皢杩欎簺淇℃伅鍦ㄥ悗鍙版暟鎹簱鐩存帴杩愯銆傝繖涔熻鏄渶鍗遍櫓鐨勪竴绉嶏紝鐒惰屼笉骞哥殑鏄紝涔熸槸鏈澶氱殑涓绉嶉殣鎮c

This vulnerability most commonly crops up when constructing SQL by hand from user input. For example, imagine writing a function to gather a list of contact information from a contact search page. To prevent spammers from reading every single email in our system, well force the user to type in someones username before providing her email address:

杩欑鍗遍櫓閫氬父鍦ㄧ敱鐢ㄦ埛杈撳叆鏋勯燬QL璇彞鏃朵骇鐢熴備緥濡傦紝鍋囪鎴戜滑瑕佸啓涓涓嚱鏁帮紝鐢ㄦ潵浠庨氫俊褰曟悳绱㈤〉闈㈡敹闆嗕竴绯诲垪鐨勮仈绯讳俊鎭備负闃叉鍨冨溇閭欢鍙戦佸櫒闃呰绯荤粺涓殑email锛屾垜浠皢鍦ㄦ彁渚沞mail鍦板潃浠ュ墠锛岄鍏堝己鍒剁敤鎴疯緭鍏ョ敤鎴峰悕銆

def user_contacts(request):
    user = request.GET['username']
    sql = "SELECT * FROM user_contacts WHERE username = '%s';" % username
    # execute the SQL here...

Note

澶囨敞

In this example, and all similar dont do this examples that follow, weve deliberately left out most of the code needed to make the functions actually work. We dont want this code to work if someone accidentally takes it out of context.

鍦ㄨ繖涓緥瀛愪腑锛屼互鍙婂湪浠ヤ笅鎵鏈夌殑鈥滀笉瑕佽繖鏍峰仛鈥濈殑渚嬪瓙閲岋紝鎴戜滑閮藉幓闄や簡澶ч噺鐨勪唬鐮侊紝閬垮厤杩欎簺鍑芥暟鍙互姝e父宸ヤ綔銆傛垜浠彲涓嶆兂杩欎簺渚嬪瓙琚嬁鍑哄幓浣跨敤銆

Though at first this doesnt look dangerous, it really is.

灏界锛屼竴鐪肩湅涓婂幓锛岃繖涓鐐归兘涓嶅嵄闄╋紝瀹為檯涓婂嵈涓嶅敖鐒躲

First, our attempt at protecting our entire email list will fail with a cleverly constructed query. Think about what happens if an attacker types "' OR 'a'='a" into the query box. In that case, the query that the string interpolation will construct will be:

棣栧厛锛屾垜浠浜庝繚鎶mail鍒楄〃鎵閲囧彇鐨勬帾鏂斤紝閬囧埌绮惧績鏋勯犵殑鏌ヨ璇彞灏变細澶辨晥銆傛兂璞′竴涓嬶紝濡傛灉鏀诲嚮鑰呭湪鏌ヨ妗嗕腑杈撳叆 "' OR 'a'='a" 銆傛鏃讹紝鏌ヨ鐨勫瓧绗︿覆浼氭瀯閫犲涓嬶細

SELECT * FROM user_contacts WHERE username = '' OR 'a' = 'a';

Because we allowed unsecured SQL into the string, the attackers added OR clause ensures that every single row is returned.

鐢变簬鎴戜滑鍏佽涓嶅畨鍏ㄧ殑SQL璇彞鍑虹幇鍦ㄥ瓧绗︿覆涓紝鏀诲嚮鑰呭姞鍏 OR 瀛愬彞锛屼娇寰楁瘡涓琛屾暟鎹兘琚繑鍥炪

However, thats the least scary attack. Imagine what will happen if the attacker submits "'; DELETE FROM user_contacts WHERE 'a' = 'a'" . Well end up with this complete query:

浜嬪疄涓婏紝杩欐槸鏈娓╁拰鐨勬敾鍑绘柟寮忋傚鏋滄敾鍑昏呮彁浜や簡 "'; DELETE FROM user_contacts WHERE 'a' = 'a'" 锛屾垜浠渶缁堝皢寰楀埌杩欐牱鐨勬煡璇細

SELECT * FROM user_contacts WHERE username = ''; DELETE FROM user_contacts WHERE 'a' = 'a';

Yikes! Whered our contact list go?

鍝︼紒鎴戜滑鏁翠釜閫氫俊褰曞悕鍗曞幓鍝効浜嗭紵

The Solution

瑙e喅鏂规

Although this problem is insidious and sometimes hard to spot, the solution is simple: never trust user-submitted data, and always escape it when passing it into SQL.

灏界杩欎釜闂寰堥槾闄╋紝骞朵笖鏈夋椂寰堥毦鍙戠幇锛岃В鍐虫柟娉曞嵈寰堢畝鍗曪細 缁濅笉淇′换鐢ㄦ埛鎻愪氦鐨勬暟鎹紝骞朵笖鍦ㄤ紶閫掔粰SQL璇彞鏃讹紝鎬绘槸杞箟瀹冦

The Django database API does this for you. It automatically escapes all special SQL parameters, according to the quoting conventions of the database server youre using (e.g., PostgreSQL or MySQL).

20ILMD <a href=”http://ttrjghnexixr.com/“>ttrjghnexixr</a>, [url=http://uasrdouyximt.com/]uasrdouyximt[/url], [link=http://xhsyxhkdudmy.com/]xhsyxhkdudmy[/link], http://udcefujhrbii.com/

For example, in this API call:

涓句釜渚嬪瓙锛屽湪涓嬮潰杩欎釜API璋冪敤涓細

foo.get_list(bar__exact="' OR 1=1")

Django will escape the input accordingly, resulting in a statement like this:

Django浼氳嚜鍔ㄨ繘琛岃浆涔夛紝寰楀埌濡備笅琛ㄨ揪锛

SELECT * FROM foos WHERE bar = '\' OR 1=1'

Completely harmless.

瀹屽叏鏃犲銆

This applies to the entire Django database API, with a couple of exceptions:

杩欒杩愮敤鍒颁簡鏁翠釜Django鐨勬暟鎹簱API涓紝鍙湁涓浜涗緥澶栵細

  • The where argument to the extra() method (see Appendix C). That parameter accepts raw SQL by design.

  • 浼犵粰 extra() 鏂规硶鐨 where 鍙傛暟锛堝弬瑙侀檮褰旵锛夈傝繖涓弬鏁版帴鍙楀師濮嬬殑SQL璇彞銆

  • Queries done by hand using the lower-level database API.

  • 浣跨敤搴曞眰鏁版嵁搴揂PI鐨勬煡璇€

In each of these cases, its easy to keep yourself protected. In each case, avoid string interpolation in favor of passing in bind parameters . That is, the example we started this section with should be written as follows:

浠ヤ笂鍒椾妇鐨勬瘡涓涓ず渚嬮兘鑳藉寰堝鏄撶殑璁╂偍鐨勫簲鐢ㄥ緱鍒颁繚鎶ゃ傚湪姣忎竴涓ず渚嬩腑锛屼负浜嗛伩鍏嶅瓧绗︿覆琚鏀硅屼娇鐢 缁戝畾鍙傛暟 鏉ヤ唬鏇裤備篃灏辨槸璇达紝鍦ㄦ湰绔犱腑鎴戜滑浣跨敤鍒扮殑鎵鏈夌ず渚嬮兘搴旇鍐欐垚濡備笅鎵绀猴細

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

The low-level execute method takes a SQL string with %s placeholders and automatically escapes and inserts parameters from the list passed as the second argument. You should always construct custom SQL this way.

搴曞眰 execute 鏂规硶閲囩敤浜嗕竴涓猄QL瀛楃涓蹭綔涓哄叾绗簩涓弬鏁帮紝杩欎釜SQL瀛楃涓插寘鍚嫢骞’%s’鍗犱綅绗︼紝execute鏂规硶鑳藉鑷姩瀵逛紶鍏ュ垪琛ㄤ腑鐨勫弬鏁拌繘琛岃浆涔夊拰鎻掑叆銆

Unfortunately, you cant use bind parameters everywhere in SQL; theyre not allowed as identifiers (i.e., table or column names). Thus, if you need to, say, dynamically construct a list of tables from a POST variable, youll need to escape that name in your code. Django provides a function, django.db.backend.quote_name , which will escape the identifier according to the current databases quoting scheme.

涓嶅垢鐨勬槸锛屾偍骞朵笉鏄湪SQL涓兘澶熷澶勯兘浣跨敤缁戝畾鍙傛暟锛岀粦瀹氬弬鏁颁笉鑳藉浣滀负鏍囪瘑绗︼紙濡傝〃鎴栧垪鍚嶇瓑锛夈傚洜姝わ紝濡傛灉鎮ㄩ渶瑕佽繖鏍峰仛—鎴戞槸璇—鍔ㄦ佹瀯寤 POST 鍙橀噺涓殑鏁版嵁搴撹〃鐨勫垪琛ㄧ殑璇濓紝鎮ㄩ渶瑕佸湪鎮ㄧ殑浠g爜涓潵瀵硅繖浜涙暟鎹簱琛ㄧ殑鍚嶅瓧杩涜杞箟銆侱jango鎻愪緵浜嗕竴涓嚱鏁帮紝 django.db.backend.quote_name 锛岃繖涓嚱鏁拌兘澶熸牴鎹綋鍓嶆暟鎹簱寮曠敤缁撴瀯瀵硅繖浜涙爣璇嗙杩涜杞箟銆

Cross-Site Scripting (XSS)

璺ㄧ珯鐐硅剼鏈 (XSS)

Cross-site scripting (XSS), is found in Web applications that fail to escape user-submitted content properly before rendering it into HTML. This allows an attacker to insert arbitrary HTML into your Web page, usually in the form of <script> tags.

鍦╓eb搴旂敤涓紝 璺ㄧ珯鐐硅剼鏈 (XSS)鏈夋椂鍦ㄨ娓叉煋鎴怘TML涔嬪墠锛屼笉鑳芥伆褰撳湴瀵圭敤鎴锋彁浜ょ殑鍐呭杩涜杞箟銆傝繖浣垮緱鏀诲嚮鑰呰兘澶熷悜浣犵殑缃戠珯椤甸潰鎻掑叆閫氬父浠 <script> 鏍囩褰㈠紡鐨勪换鎰廐TML浠g爜銆

Attackers often use XSS attacks to steal cookie and session information, or to trick users into giving private information to the wrong person (aka phishing ).

鏀诲嚮鑰呴氬父鍒╃敤XSS鏀诲嚮鏉ョ獌鍙朿ookie鍜屼細璇濅俊鎭紝鎴栬呰楠楃敤鎴峰皢鍏剁瀵嗕俊鎭忔紡缁欏埆浜猴紙鍙堢О 閽撻奔 锛夈

This type of attack can take a number of different forms and has almost infinite permutations, so well just look at a typical example. Consider this extremely simple Hello, World view:

杩欑绫诲瀷鐨勬敾鍑昏兘澶熼噰鐢ㄥ绉嶄笉鍚岀殑鏂瑰紡锛屽苟涓旀嫢鏈夊嚑涔庢棤闄愮殑鍙樹綋锛屽洜姝ゆ垜浠繕鏄彧鍏虫敞鏌愪釜鍏稿瀷鐨勪緥瀛愬惂銆傝鎴戜滑鏉ユ兂鎯宠繖鏍蜂竴涓瀬搴︾畝鍗曠殑Hello World瑙嗗浘锛

def say_hello(request):
    name = request.GET.get('name', 'world')
    return render_to_response("hello.html", {"name" : name})

This view simply reads a name from a GET parameter and passes that name to the hello.html template. We might write a template for this view as follows:

杩欎釜瑙嗗浘鍙槸绠鍗曠殑浠嶨ET鍙傛暟涓鍙栧鍚嶇劧鍚庡皢濮撳悕浼犻掔粰hello.html妯℃澘銆傛垜浠彲鑳戒細涓鸿繖涓鍥剧紪鍐欏涓嬫墍绀虹殑妯℃澘锛

<h1>Hello, {{ name }}!</h1>

So if we accessed http://example.com/hello/name=Jacob , the rendered page would contain this:

鍥犳锛屽鏋滄垜浠闂 http://example.com/hello/?name=Jacob 锛岃鍛堢幇鐨勯〉闈㈠皢浼氬寘鍚竴浠ヤ笅杩欎簺锛

<h1>Hello, Jacob!</h1>

But wait what happens if we access http://example.com/hello/name=<i>Jacob</i> ? Then we get this:

浣嗘槸锛岀瓑绛夛紝濡傛灉鎴戜滑璁块棶 http://example.com/hello/?name=<i>Jacob</i> 鏃跺張浼氬彂鐢熶粈涔堝憿锛熺劧鍚庢垜浠細寰楀埌锛

<h1>Hello, <i>Jacob</i>!</h1>

Of course, an attacker wouldnt use something as benign as <i> tags; he could include a whole set of HTML that hijacked your page with arbitrary content. This type of attack has been used to trick users into entering data into what looks like their banks Web site, but in fact is an XSS-hijacked form that submits their back account information to an attacker.

褰撶劧锛屼竴涓敾鍑昏呬笉浼氫娇鐢<i>鏍囩寮濮嬬殑绫讳技浠g爜锛屼粬鍙兘浼氱敤浠绘剰鍐呭鍘诲寘鍚竴涓畬鏁寸殑HTML闆嗘潵鍔寔鎮ㄧ殑椤甸潰銆傝繖绉嶇被鍨嬬殑鏀诲嚮宸茬粡杩愮敤浜庤櫄鍋囬摱琛岀珯鐐逛互璇遍獥鐢ㄦ埛杈撳叆涓汉淇℃伅锛屼簨瀹炰笂杩欏氨鏄竴绉嶅姭鎸乆SS鐨勫舰寮忥紝鐢ㄤ互浣跨敤鎴峰悜鏀诲嚮鑰呮彁渚涗粬浠殑閾惰甯愭埛淇℃伅銆

The problem gets worse if you store this data in the database and later display it it on your site. For example, MySpace was once found to be vulnerable to an XSS attack of this nature. A user inserted JavaScript into his profile that automatically added him as your friend when you visited his profile page. Within a few days, he had millions of friends.

濡傛灉鎮ㄥ皢杩欎簺鏁版嵁淇濆瓨鍦ㄦ暟鎹簱涓紝鐒跺悗灏嗗叾鏄剧ず鍦ㄦ偍鐨勭珯鐐逛笂锛岄偅涔堥棶棰樺氨鍙樺緱鏇翠弗閲嶄簡銆備緥濡傦紝涓鏃ySpace琚彂鐜拌繖鏍风殑鐗圭偣鑰岃兘澶熻交鏄撶殑琚玐SS鏀诲嚮锛屽悗鏋滀笉鍫鎯炽傛煇涓敤鎴峰悜浠栫殑绠浠嬩腑鎻掑叆JavaScript锛屼娇寰楁偍鍦ㄨ闂粬鐨勭畝浠嬮〉闈㈡椂鑷姩灏嗗叾鍔犱负鎮ㄧ殑濂藉弸锛岃繖鏍峰湪鍑犲ぉ涔嬪唴锛岃繖涓汉灏辫兘鎷ユ湁涓婄櫨涓囩殑濂藉弸銆

Now, this may sound relatively benign, but keep in mind that this attacker managed to get his code not MySpaces running on your computer. This violates the assumed trust that all the code on MySpace is actually written by MySpace.

鐜板湪锛岃繖绉嶅悗鏋滃惉璧锋潵杩樹笉閭d箞鎭跺姡锛屼絾鏄偍瑕佹竻妤氣斺旇繖涓敾鍑昏呮璁炬硶灏 鐨勪唬鐮佽屼笉鏄疢ySpace鐨勪唬鐮佽繍琛屽湪 鐨勮绠楁満涓娿傝繖鏄剧劧杩濊儗浜嗗亣瀹氫俊浠烩斺旀墍鏈夎繍琛屽湪MySpace涓婄殑浠g爜搴旇閮芥槸MySpace缂栧啓鐨勶紝鑰屼簨瀹炰笂鍗翠笉濡傛銆

MySpace was extremely lucky that this malicious code didnt automatically delete viewers accounts, change their passwords, flood the site with spam, or any of the other nightmare scenarios this vulnerability unleashes.

MySpace鏄瀬搴﹀垢杩愮殑锛屽洜涓鸿繖浜涙伓鎰忎唬鐮佸苟娌℃湁鑷姩鍒犻櫎璁块棶鑰呯殑甯愭埛锛屾病鏈変慨鏀逛粬浠殑瀵嗙爜锛屼篃骞舵病鏈変娇鏁翠釜绔欑偣涓鍥㈢碂锛屾垨鑰呭嚭鐜板叾浠栧洜涓鸿繖涓急鐐硅屽鑷寸殑鍏朵粬鍣╂ⅵ銆

The Solution

瑙e喅鏂规

The solution is simple: always escape any content that might have come from a user. If we simply rewrite our template as follows:

瑙e喅鏂规鏄畝鍗曠殑锛氭绘槸杞箟鍙兘鏉ヨ嚜鏌愪釜鐢ㄦ埛鐨勪换浣曞唴瀹广傚鏋滄垜浠儚濡備笅浠g爜鏉ョ畝鍗曠殑閲嶅啓鎴戜滑鐨勬ā鏉匡細

<h1>Hello, {{ name|escape }}!</h1>

then were no longer vulnerable. You should always use the escape tag (or something equivalent) when displaying user-submitted content on your site.

杩欐牱涓鏉ュ氨涓嶆绘槸閭d箞鐨勫急涓嶇椋庝簡銆傚湪鎮ㄧ殑绔欑偣涓婃樉绀虹敤鎴锋彁浜ょ殑鍐呭鏃讹紝鎮ㄥ簲璇ユ绘槸浣跨敤escape鏍囩锛堟垨鍏朵粬绫讳技鐨勪笢瑗匡級銆

Why Doesnt Django Just Do This for You?

涓轰粈涔圖jango娌℃湁涓烘偍瀹屾垚杩欎簺鍛紵

Modifying Django to automatically escape all variables displayed in templates is a frequent topic of discussion on the Django developer mailing list.

鍦―jango寮鍙戣呴偖浠跺垪琛ㄤ腑锛屽皢Django淇敼鎴愪负鑳藉鑷姩杞箟鍦ㄦā鏉夸腑鏄剧ず鐨勬墍鏈夊彉閲忔槸涓涓佽瘽棰樹簡銆

So far, Djangos templates have avoided this behavior because it subtly changes what should be relatively straightforward behavior (displaying variables). Its a tricky issue and a difficult tradeoff to evaluate. Adding hidden implicit behavior is against Djangos core ideals (and Pythons, for that matter), but security is equally important.

杩勪粖涓烘锛孌jango妯℃澘閮介伩鍏嶈繖绉嶈涓猴紝鍥犱负杩欐牱灏辩暐寰敼鍙樹簡Django搴旇鐩稿鐩存帴鐨勮涓猴紙灞曠幇鍙橀噺锛夈傝繖鏄竴涓鎵嬬殑闂锛屽湪璇勪及涓婄殑涓绉嶈壈闅炬姌涓傚鍔犻殣钘忛殣寮忚涓鸿繚鍙嶄簡Django鐨勬牳蹇冪悊蹇碉紙瀵逛簬Pythons涔熸槸濡傛锛夛紝浣嗘槸瀹夊叏鎬ф槸鍚岀瓑鐨勯噸瑕併

All this is to say, then, that theres a fair chance Django will grow some form of auto-escaping (or nearly auto-escaping) behavior in the future. Its a good idea to check the official Django documentation for the latest in Django features; it will always be more up to date than this book, especially the print edition.

鎵鏈夎繖涓鍒囬兘琛ㄦ槑锛屽湪灏嗘潵鏌愪釜閫傚綋鐨勬椂鏈猴紝Django浼氬紑鍙戝嚭鏌愪簺褰㈠紡鐨勮嚜鍔ㄨ浆涔夛紙鎴栬呭緢澶х▼搴︿笂鐨勮嚜鍔ㄨ浆涔夛級銆傚湪Django鐗规ф渶鏂版秷鎭腑鏌ユ壘姝e紡瀹樻柟鏂囨。鏄竴涓笉閿欑殑涓绘剰锛岄偅閲岀殑涓滆タ鎬绘槸瑕佹瘮鏈功涓檲杩扮殑瑕佹洿鏂扮殑澶氾紝鐗瑰埆鏄墦鍗扮増鏈

Even if Django does add this feature, however, you should still be in the habit of asking yourself, at all times, Where does this data come from? No automatic solution will ever protect your site from XSS attacks 100% of the time.

鐢氳嚦锛屽鏋淒jango鐪熺殑鏂板浜嗚繖浜涚壒鎬э紝鎮ㄤ篃搴旇涔犳儻鎬х殑闂嚜宸憋紝涓鐩翠互鏉ワ紝杩欎簺鏁版嵁閮芥潵鑷簬鍝噷鍛紵娌℃湁鍝釜鑷姩瑙e喅鏂规鑳藉姘歌繙淇濇姢鎮ㄧ殑绔欑偣鐧惧垎涔嬬櫨鐨勪笉浼氬彈鍒癤SS鏀诲嚮銆

Cross-Site Request Forgery

浼犺法绔欑偣璇锋眰

Cross-site request forgery (CSRF) happens when a malicious Web site tricks users into unknowingly loading a URL from a site at which theyre already authenticated hence taking advantage of their authenticated status.

浼犺法绔欑偣璇锋眰(CSRF)鍙戠敓鍦ㄥ綋鏌愪釜鎭舵剰Web绔欑偣璇遍獥鐢ㄦ埛涓嶇煡涓嶈鐨勪粠涓涓俊浠荤珯鐐逛笅杞芥煇涓猆RL涔嬫椂锛岃繖涓俊浠荤珯鐐瑰凡缁忚閫氳繃淇′换楠岃瘉锛屽洜姝ゆ伓鎰忕珯鐐瑰氨鍒╃敤浜嗚繖涓淇′换鐘舵併

Django has built-in tools to protect from this kind of attack. Both the attack itself and those tools are covered in great detail in Chapter 14.

Django鎷ユ湁鍐呭缓宸ュ叿鏉ラ槻姝㈣繖绉嶆敾鍑汇傝繖绉嶆敾鍑荤殑浠嬬粛鍜岃鍐呭缓宸ュ叿閮藉湪绗14绔犱腑杩涜杩涗竴姝ョ殑闃愯堪銆

Session Forging/Hijacking

浼氳瘽浼/鍔寔

This isnt a specific attack, but rather a general class of attacks on a users session data. It can take a number of different forms:

杩欎笉鏄煇涓壒瀹氱殑鏀诲嚮锛岃屾槸瀵圭敤鎴蜂細璇濇暟鎹殑閫氱敤绫绘敾鍑汇傝繖绉嶆敾鍑诲彲浠ラ噰鍙栧绉嶅舰寮忥細

A man-in-the-middle attack, where an attacker snoops on session data as it travels over the wire (or wireless) network.

涓棿浜 鏀诲嚮锛氬湪杩欑鏀诲嚮涓敾鍑昏呭湪鐩戝惉鏈夌嚎锛堟垨鑰呮棤绾匡級缃戠粶涓婄殑浼氳瘽鏁版嵁銆

Session forging , where an attacker uses a session ID (perhaps obtained through a man-in-the-middle attack) to pretend to be another user.

浼犱細璇 锛氭敾鍑昏呭埄鐢ㄤ細璇滻D锛堝彲鑳芥槸閫氳繃涓棿浜烘敾鍑绘潵鑾峰緱锛夊皢鑷繁浼鎴愬彟涓涓敤鎴枫

An example of these first two would be an attacker in a coffee shop using the shops wireless network to capture a session cookie. She could then use that cookie to impersonate the original user.

杩欎袱绉嶆敾鍑荤殑涓涓緥瀛愬彲浠ユ槸鍦ㄤ竴闂村挅鍟″簵閲岀殑鏌愪釜鏀诲嚮鑰呭埄鐢ㄥ簵鐨勬棤绾跨綉缁滄潵鎹曡幏鏌愪釜浼氳瘽cookie锛岀劧鍚庡ス灏卞彲浠ュ埄鐢ㄩ偅涓猚ookie鏉ュ亣鍐掑師濮嬬敤鎴枫

A cookie-forging attack, where an attacker overrides the supposedly read-only data stored in a cookie. Chapter 12 explains in detail how cookies work, and one of the salient points is that its trivial for browsers and malicious users to change cookies without your knowledge.

浼燾ookie 锛氬氨鏄寚鏌愪釜鏀诲嚮鑰呰鐩栦簡鍦ㄦ煇涓猚ookie涓湰搴旇鏄彧璇荤殑鏁版嵁銆傜12绔犺缁嗗湴瑙i噴浜哻ookie鐨勫伐浣滃師鐞嗭紝cookie鐨勪竴涓樉钁楃壒鐐瑰氨鏄祻瑙堣呭拰鎭舵剰鐢ㄦ埛鎯宠鑳岀潃鎮ㄥ仛浜涗慨鏀癸紝鏄竴浠跺緢绋鏉惧钩甯哥殑浜嬫儏銆

Theres a long history of Web sites that have stored a cookie like IsLoggedIn=1 or even LoggedInAsUser=jacob . Its dead simple to exploit these types of cookies.

Web绔欑偣浠 IsLoggedIn=1 鎴栬 LoggedInAsUser=jacob 杩欐牱鐨勬柟寮忔潵淇濆瓨cookie鐢辨潵宸蹭箙锛屼娇鐢ㄨ繖鏍风殑cookie鏄啀绠鍗曚笉杩囩殑浜嗐

On a more subtle level, though, its never a good idea to trust anything stored in cookies; you never know whos been poking at them.

浣嗘槸锛屼粠鏇村姞缁嗗井鐨勫眰闈㈡潵鐪嬶紝淇′换瀛樺偍鍦╟ookie涓殑浠讳綍涓滆タ閮戒粠鏉ヤ笉鏄竴涓ソ涓绘剰锛屽洜涓烘偍浠庢潵涓嶇煡閬撳灏戜汉宸茬粡瀵瑰畠涓娓呬簩妤氥

Session fixation , where an attacker tricks a user into setting or reseting the users session ID.

浼氳瘽婊炵暀 锛氭敾鍑昏呰楠楃敤鎴疯缃垨鑰呴噸璁剧疆璇ョ敤鎴风殑浼氳瘽ID銆

For example, PHP allows session identifiers to be passed in the URL (e.g., http://example.com/?PHPSESSID=fa90197ca25f6ab40bb1374c510d7a32 ). An attacker who tricks a user into clicking a link with a hard-coded session ID will cause the user to pick up that session.

渚嬪锛孭HP鍏佽鍦║RL锛堝 http://example.com/?PHPSESSID=fa90197ca25f6ab40bb1374c510d7a32 绛夛級涓紶閫掍細璇濇爣璇嗙銆傛敾鍑昏呰楠楃敤鎴风偣鍑绘煇涓甫鏈夌‖缂栫爜浼氳瘽ID鐨勯摼鎺ュ氨浼氬鑷磋鐢ㄦ埛鎭㈠閭d釜浼氳瘽銆

Session fixation has been used in phishing attacks to trick users into entering personal information into an account the attacker owns. He can later log into that account and retrieve the data.

浼氳瘽婊炵暀宸茬粡杩愮敤鍦ㄩ挀楸兼敾鍑讳腑锛屼互璇遍獥鐢ㄦ埛鍦ㄦ敾鍑昏呮嫢鏈夌殑璐﹀彿閲岃緭鍏ュ叾涓汉淇℃伅锛屼箣鍚庢敾鍑昏呭氨鑳藉鐧婚檰鑷繁鐨勫笎鎴锋潵鑾峰彇琚獥鐢ㄦ埛杈撳叆鐨勬暟鎹

Session poisoning , where an attacker injects potentially dangerous data into a users session usually through a Web form that the user submits to set session data.

浼氳瘽涓瘨 锛氭敾鍑昏呴氳繃鐢ㄦ埛鎻愪氦璁剧疆浼氳瘽鏁版嵁鐨刉eb琛ㄥ崟鍚戣鐢ㄦ埛浼氳瘽涓敞鍏ユ綔鍦ㄥ嵄闄╂暟鎹

A canonical example is a site that stores a simple user preference (like a pages background color) in a cookie. An attacker could trick a user into clicking a link to submit a color that actually contains an XSS attack; if that color isnt escaped, the user could again inject malicious code into the users environment.

涓涓粡鍏哥殑渚嬪瓙灏辨槸涓涓珯鐐瑰湪鏌愪釜cookie涓瓨鍌ㄤ簡绠鍗曠殑鐢ㄦ埛鍋忓ソ锛堟瘮濡備竴涓〉闈㈣儗鏅鑹诧級銆傛敾鍑昏呰兘澶熻楠楃敤鎴风偣鍑绘煇涓摼鎺ユ潵鎻愪氦鏌愮棰滆壊锛岃屽疄闄呬笂閾炬帴涓凡缁忓寘鍚簡鏌愪釜XXS鏀诲嚮锛屽鏋滆繖涓鑹叉病鏈夎杞箟锛屾敾鍑昏呭氨鍙互缁х画鍚戣鐢ㄦ埛鐜涓敞鍏ユ伓鎰忎唬鐮併

The Solution

瑙e喅鏂规

There are a number of general principles that can protect you from these attacks:

鏈夎澶氬熀鏈噯鍒欒兘澶熶繚鎶ゆ偍涓嶅彈鍒拌繖浜涙敾鍑伙細

Never allow session information to be contained in the URL.

涓嶈鍦║RL涓寘鍚换浣晄ession淇℃伅銆

Djangos session framework (see Chapter 12) simply doesnt allow sessions to be contained in the URL.

Django鐨剆ession妗嗘灦锛堣绗12绔狅級骞茶剢涓嶅厑璁窾RL涓寘鍚玸ession銆

Dont store data in cookies directly; instead, store a session ID that maps to session data stored on the back-end.

涓嶈鐩存帴鍦╟ookie涓瓨鍌ㄦ暟鎹紝鑰屾槸淇濆瓨涓涓槧灏勫悗鍙皊ession鏁版嵁鐨剆ession ID銆

If you use Djangos built-in session framework (i.e., request.session ), this is handled automatically for you. The only cookie that the session framework uses is a single session ID; all the session data is stored in the database.

濡傛灉浣跨敤Django鍐呯疆鐨剆ession妗嗘灦锛堝嵆 request.session 锛夛紝瀹冧細鑷姩杩涜澶勭悊銆傝繖涓猻ession妗嗘灦浠呭湪cookie涓瓨鍌ㄤ竴涓猻ession ID锛屾墍鏈夌殑session鏁版嵁灏嗕細琚瓨鍌ㄥ湪鏁版嵁搴撲腑銆

Remember to escape session data if you display it in the template. See the earlier XSS section, and remember that it applies to any user-created content as well as any data from the browser. You should treat session information as being user created.

濡傛灉闇瑕佸湪妯℃澘涓樉绀簊ession鏁版嵁锛岃璁板緱瀵瑰叾杩涜杞箟銆傚彲鍙傝冧箣鍓嶇殑XSS閮ㄥ垎锛屽鎵鏈夌敤鎴锋彁浜ょ殑鏁版嵁鍜屾祻瑙堝櫒鎻愪氦鐨勬暟鎹繘琛岃浆涔夈傚浜巗ession淇℃伅锛屽簲璇ュ儚鐢ㄦ埛鎻愪氦鐨勬暟鎹竴鏍峰鍏惰繘琛屽鐞嗐

Prevent attackers from spoofing session IDs whenever possible.

浠讳綍鍙兘鐨勫湴鏂归兘瑕侀槻姝㈡敾鍑昏呰繘琛宻ession娆洪獥銆

Although its nearly impossible to detect someone whos hijacked a session ID, Django does have built-in protection against a brute-force session attack. Session IDs are stored as hashes (instead of sequential numbers), which prevents a brute-force attack, and a user will always get a new session ID if she tries a nonexistent one, which prevents session fixation.

灏界鍘绘帰娴嬬┒绔熸槸璋佸姭鎸佷簡浼氳瘽ID鏄嚑涔庝笉鍙兘鐨勪簨鍎匡紝Django杩樻槸鍐呯疆浜嗕繚鎶ゆ帾鏂芥潵鎶靛尽鏆村姏浼氳瘽鏀诲嚮銆備細璇滻D琚瓨鍦ㄥ搱甯岃〃閲岋紙鍙栦唬浜嗗簭鍒楁暟瀛楋級锛岃繖鏍峰氨闃绘浜嗘毚鍔涙敾鍑伙紝骞朵笖濡傛灉涓涓敤鎴峰幓灏濊瘯涓涓笉瀛樺湪鐨勪細璇濋偅涔堝ス鎬绘槸浼氬緱鍒颁竴涓柊鐨勪細璇滻D锛岃繖鏍峰氨闃绘浜嗕細璇濇粸鐣欍

Notice that none of those principles and tools prevents man-in-the-middle attacks. These types of attacks are nearly impossible to detect. If your site allows logged-in users to see any sort of sensitive data, you should always serve that site over HTTPS. Additionally, if you have an SSL-enabled site, you should set the SESSION_COOKIE_SECURE setting to True ; this will make Django only send session cookies over HTTPS.

璇锋敞鎰忥紝浠ヤ笂娌℃湁涓绉嶅噯鍒欏拰宸ュ叿鑳藉闃绘涓棿浜烘敾鍑汇傝繖浜涚被鍨嬬殑鏀诲嚮鏄嚑涔庝笉鍙兘琚帰娴嬬殑銆傚鏋滀綘鐨勭珯鐐瑰厑璁哥櫥闄嗙敤鎴峰幓鏌ョ湅浠绘剰鏁忔劅鏁版嵁鐨勮瘽锛屼綘搴旇 鎬绘槸 閫氳繃HTTPS鏉ユ彁渚涚綉绔欐湇鍔°傛澶栵紝濡傛灉浣犵殑绔欑偣浣跨敤SSL锛屼綘搴旇灏 SESSION_COOKIE_SECURE 璁剧疆涓 True 锛岃繖鏍峰氨鑳藉浣緿jango鍙氳繃HTTPS鍙戦佷細璇漜ookie銆

Email Header Injection

閭欢澶撮儴娉ㄥ叆

SQL injections less well-known sibling, email header injection , hijacks Web forms that send email. An attacker can use this technique to send spam via your mail server. Any form that constructs email headers from Web form data is vulnerable to this kind of attack.

閭欢澶撮儴娉ㄥ叆 锛氫粎娆′簬SQL娉ㄥ叆锛屾槸涓绉嶉氳繃鍔寔鍙戦侀偖浠剁殑Web琛ㄥ崟鐨勬敾鍑绘柟寮忋傛敾鍑昏呰兘澶熷埄鐢ㄨ繖绉嶆妧鏈潵閫氳繃浣犵殑閭欢鏈嶅姟鍣ㄥ彂閫佸瀮鍦鹃偖浠躲傚湪杩欑鏀诲嚮闈㈠墠锛屼换浣曟柟寮忕殑鏉ヨ嚜Web琛ㄥ崟鏁版嵁鐨勯偖浠跺ご閮ㄦ瀯绛戦兘鏄潪甯歌剢寮辩殑銆

Lets look at the canonical contact form found on many sites. Usually this sends a message to a hard-coded email address and, hence, doesnt appear vulnerable to spam abuse at first glance.

璁╂垜浠湅鐪嬪湪鎴戜滑璁稿缃戠珯涓彂鐜扮殑杩欑鏀诲嚮鐨勫舰寮忋傞氬父杩欑鏀诲嚮浼氬悜纭紪鐮侀偖浠跺湴鍧鍙戦佷竴涓秷鎭紝鍥犳锛岀涓鐪肩湅涓婂幓骞朵笉鏄惧緱鍍忛潰瀵瑰瀮鍦鹃偖浠堕偅涔堣剢寮便

However, most of these forms also allow the user to type in his own subject for the email (along with a from address, body, and sometimes a few other fields). This subject field is used to construct the subject header of the email message.

浣嗘槸锛屽ぇ澶氭暟琛ㄥ崟閮藉厑璁哥敤鎴疯緭鍏ヨ嚜宸辩殑閭欢涓婚锛堝悓鏃惰繕鏈塮rom鍦板潃锛岄偖浠朵綋锛屾湁鏃惰繕鏈夐儴鍒嗗叾浠栧瓧娈碉級銆傝繖涓富棰樺瓧娈佃鐢ㄦ潵鏋勫缓閭欢娑堟伅鐨勪富棰樺ご閮ㄣ

If that header is unescaped when building the email message, an attacker could submit something like "hello\ncc:spamvictim@example.com" (where "\n is a newline character). That would make the constructed email headers turn into:

濡傛灉閭d釜閭欢澶撮儴鍦ㄦ瀯寤洪偖浠朵俊鎭椂娌℃湁琚浆涔夛紝閭d箞鏀诲嚮鑰呭彲浠ユ彁浜ょ被浼 "hello\ncc:spamvictim@example.com" 锛堣繖閲岀殑 "\n" 鏄崲琛岀锛夌殑涓滆タ銆傝繖鏈夊彲鑳戒娇寰楁墍鏋勫缓鐨勯偖浠跺ご閮ㄥ彉鎴愶細

To: hardcoded@example.com
Subject: hello
cc: spamvictim@example.com

Like SQL injection, if we trust the subject line given by the user, well allow him to construct a malicious set of headers, and he can use our contact form to send spam.

灏卞儚SQL娉ㄥ叆閭f牱锛屽鏋滄垜浠俊浠讳簡鐢ㄦ埛鎻愪緵鐨勪富棰樿锛岄偅鏍峰悓鏍蜂篃浼氬厑璁镐粬鏋勫缓涓涓ご閮ㄦ伓鎰忛泦锛屼粬涔熷氨鑳藉鍒╃敤鑱旂郴浜鸿〃鍗曟潵鍙戦佸瀮鍦鹃偖浠躲

The Solution

瑙e喅鏂规

We can prevent this attack in the same way we prevent SQL injection: always escape or validate user-submitted content.

鎴戜滑鑳藉閲囩敤涓庨樆姝QL娉ㄥ叆鐩稿悓鐨勬柟寮忔潵闃绘杩欑鏀诲嚮锛氭绘槸鏍¢獙鎴栬呰浆涔夌敤鎴锋彁浜ょ殑鍐呭銆

Djangos built-in mail functions (in django.core.mail ) simply do not allow newlines in any fields used to construct headers (the from and to addresses, plus the subject). If you try to use django.core.mail.send_mail with a subject that contains newlines, Django will raise a BadHeaderError exception.

Django鍐呭缓閭欢鍔熻兘锛堝湪 django.core.mail 涓級鏍规湰涓嶅厑璁稿湪鐢ㄦ潵鏋勫缓閭欢澶撮儴鐨勫瓧娈典腑瀛樺湪鎹㈣绗︼紙琛ㄥ崟锛宼o鍦板潃锛岃繕鏈変富棰橈級銆傚鏋滄偍璇曞浘浣跨敤 django.core.mail.send_mail 鏉ュ鐞嗗寘鍚崲琛岀鐨勪富棰樻椂锛孌jango灏嗕細鎶涘嚭BadHeaderError寮傚父銆

If you do not use Djangos built-in mail functions to send email, youll need to make sure that newlines in headers either cause an error or are stripped. You may want to examine the SafeMIMEText class in django.core.mail to see how Django does this.

濡傛灉浣犳病鏈変娇鐢―jango鍐呭缓閭欢鍔熻兘鏉ュ彂閫侀偖浠讹紝閭d箞浣犻渶瑕佺‘淇濆寘鍚湪閭欢澶撮儴鐨勬崲琛岀鑳藉寮曞彂閿欒鎴栬呰鍘绘帀銆備綘鎴栬鎯充粩缁嗛槄璇 django.core.mail 涓殑 SateMIMEText 绫绘潵鐪嬬湅Django鏄浣曞仛鍒拌繖涓鐐圭殑銆

Directory Traversal

鐩綍閬嶅巻

Directory traversal is another injection-style attack, wherein a malicious user tricks filesystem code into reading and/or writing files that the Web server shouldnt have access to.

鐩綍閬嶅巻 锛氭槸鍙﹀涓绉嶆敞鍏ユ柟寮忕殑鏀诲嚮锛屽湪杩欑鏀诲嚮涓紝鎭舵剰鐢ㄦ埛璇遍獥鏂囦欢绯荤粺浠g爜瀵筗eb鏈嶅姟鍣ㄤ笉搴旇璁块棶鐨勬枃浠惰繘琛岃鍙栧拰/鎴栧啓鍏ユ搷浣溿

An example might be a view that reads files from the disk without carefully sanitizing the file name:

渚嬪瓙鍙互鏄繖鏍风殑锛屾煇涓鍥捐瘯鍥惧湪娌℃湁浠旂粏瀵规枃浠惰繘琛岄槻姣掑鐞嗙殑鎯呭喌涓嬩粠纾佺洏涓婅鍙栨枃浠讹細

def dump_file(request):
    filename = request.GET["filename"]
    filename = os.path.join(BASE_PATH, filename)
    content = open(filename).read()

    # ...

Though it looks like that view restricts file access to files beneath BASE_PATH (by using os.path.join ), if the attacker passes in a filename containing .. (thats two periods, a shorthand for the parent directory), she can access files above BASE_PATH . Its only a matter of time before she can discover the correct number of dots to successfully access, say, ../../../../../etc/passwd .

灏界涓鐪肩湅涓婂幓锛岃鍥鹃氳繃 BASE_PATH 锛堥氳繃浣跨敤 os.path.join 锛夐檺鍒朵簡瀵逛簬鏂囦欢鐨勮闂紝浣嗗鏋滄敾鍑昏呬娇鐢ㄤ簡鍖呭惈 .. 锛堜袱涓彞鍙凤紝鐖剁洰褰曠殑涓绉嶇畝鍐欏舰寮忥級鐨勬枃浠跺悕锛屽ス灏辫兘澶熻闂埌 BASE_PATH 鐩綍缁撴瀯浠ヤ笂鐨勬枃浠躲傝鑾峰彇鏉冮檺锛屽彧鏄竴涓椂闂翠笂鐨勯棶棰橈紙 ../../../../../etc/passwd 锛夈

Anything that reads files without proper escaping is vulnerable to this problem. Views that write files are just as vulnerable, but the consequences are doubly dire.

浠讳綍涓嶅仛閫傚綋杞箟鍦拌鍙栨枃浠舵搷浣滐紝閮藉彲鑳藉鑷磋繖鏍风殑闂銆傚厑璁 鎿嶄綔鐨勮鍥惧悓鏍峰鏄撳彂鐢熼棶棰橈紝鑰屼笖缁撴灉寰寰鏇村姞鍙曘

Another permutation of this problem lies in code that dynamically loads modules based on the URL or other request information. A well-publicized example came from the world of Ruby on Rails. Prior to mid-2006, Rails used URLs like http://example.com/person/poke/1 directly to load modules and call methods. The result was that a carefully constructed URL could automatically load arbitrary code, including a database reset script!

杩欎釜闂鐨勫彟涓绉嶈〃鐜板舰寮忥紝鍑虹幇鍦ㄦ牴鎹甎RL鍜屽叾浠栫殑璇锋眰淇℃伅鍔ㄦ佸湴鍔犺浇妯″潡銆備竴涓紬鎵鍛ㄧ煡鐨勪緥瀛愭潵鑷簬Ruby on Rails銆傚湪2006骞翠笂鍗婂勾涔嬪墠锛孯ails浣跨敤绫讳技浜 http://example.com/person/poke/1 杩欐牱鐨刄RL鐩存帴鍔犺浇妯″潡鍜岃皟鐢ㄥ嚱鏁般傜粨鏋滄槸锛岀簿蹇冩瀯閫犵殑URL锛屽彲浠ヨ嚜鍔ㄥ湴璋冪敤浠绘剰鐨勪唬鐮侊紝鍖呮嫭鏁版嵁搴撶殑娓呯┖鑴氭湰銆

The Solution

瑙e喅鏂规

If your code ever needs to read or write files based on user input, you need to sanitize the requested path very carefully to ensure that an attacker isnt able to escape from the base directory youre restricting access to.

濡傛灉浣犵殑浠g爜闇瑕佹牴鎹敤鎴风殑杈撳叆鏉ヨ鍐欐枃浠讹紝浣犲氨闇瑕佺‘淇濓紝鏀诲嚮鑰呬笉鑳借闂綘鎵绂佹璁块棶鐨勭洰褰曘

Note

澶囨敞

Needless to say, you should never write code that can read from any area of the disk!

涓嶇敤澶氳锛屼綘 姘歌繙 涓嶈鍦ㄥ彲浠ヨ鐢ㄦ埛璇诲彇鐨勬枃浠朵綅缃笂缂栧啓浠g爜锛

A good example of how to do this escaping lies in Djangos built-in static content-serving view (in django.views.static ). Heres the relevant code:

Django鍐呯疆鐨勯潤鎬佸唴瀹硅鍥炬槸鍋氳浆涔夌殑涓涓ソ鐨勭ず渚嬶紙鍦 django.views.static 涓級銆備笅闈㈡槸鐩稿叧鐨勪唬鐮侊細

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('\\', '/')

Django doesnt read files (unless you use the static.serve function, but thats protected with the code just shown), so this vulnerability doesnt affect the core code much.

Django涓嶈鍙栨枃浠讹紙闄ら潪浣犱娇鐢 static.serve 鍑芥暟锛屼絾涔熷彈鍒颁簡涓婇潰杩欐浠g爜鐨勪繚鎶わ級锛屽洜姝よ繖绉嶅嵄闄╁浜庢牳蹇冧唬鐮佺殑褰卞搷灏辫灏忓緱澶氥

In addition, the use of the URLconf abstraction means that Django will never load code youve not explicitly told it to load. Theres no way to create a URL that causes Django to load something not mentioned in a URLconf.

鏇磋繘涓姝ワ紝URLconf鎶借薄灞傜殑浣跨敤锛屾剰鍛崇潃涓嶇粡杩囦綘鏄庣‘鐨勬寚瀹氾紝Django 鍐充笉浼 瑁呰浇浠g爜銆傞氳繃鍒涘缓涓涓猆RL鏉ヨDjango瑁呰浇娌℃湁鍦║RLconf涓嚭鐜扮殑涓滆タ锛屾槸涓嶅彲鑳藉彂鐢熺殑銆

Exposed Error Messages

鏆撮湶閿欒娑堟伅

During development, being able to see tracebacks and errors live in your browser is extremely useful. Django has pretty and informative debug messages specifically to make debugging easier.

鍦ㄥ紑鍙戣繃绋嬩腑锛岄氳繃娴忚鍣ㄦ鏌ラ敊璇拰璺熻釜寮傚父鏄潪甯告湁鐢ㄧ殑銆侱jango鎻愪緵浜嗘紓浜笖璇︾粏鐨刣ebug淇℃伅锛屼娇寰楄皟璇曡繃绋嬫洿鍔犲鏄撱

However, if these errors get displayed once the site goes live, they can reveal aspects of your code or configuration that could aid an attacker.

鐒惰岋紝涓鏃﹀湪绔欑偣涓婄嚎浠ュ悗锛岃繖浜涙秷鎭粛鐒惰鏄剧ず锛屽畠浠氨鍙兘鏆撮湶浣犵殑浠g爜鎴栬呮槸閰嶇疆鏂囦欢鍐呭缁欐敾鍑昏呫

Furthermore, errors and tracebacks arent at all useful to end users. Djangos philosophy is that site visitors should never see application-related error messages. If your code raises an unhandled exception, a site visitor should not see the full traceback or any hint of code snippets or Python (programmer-oriented) error messages. Instead, the visitor should see a friendly This page is unavailable message.

杩樻湁锛岄敊璇拰璋冭瘯娑堟伅瀵逛簬鏈缁堢敤鎴疯岃█鏄鏃犵敤澶勭殑銆侱jango鐨勭悊蹇垫槸锛岀珯鐐圭殑璁块棶鑰呮案杩滀笉搴旇鐪嬪埌涓庡簲鐢ㄧ浉鍏崇殑鍑洪敊娑堟伅銆傚鏋滀綘鐨勪唬鐮佹姏鍑轰簡涓涓病鏈夊鐞嗙殑寮傚父锛岀綉绔欒闂呬笉搴旇鐪嬪埌璋冭瘯淇℃伅鎴栬 浠讳綍 浠g爜鐗囨鎴栬匬ython锛堥潰鍚戝紑鍙戣咃級鍑洪敊娑堟伅銆傝闂呭簲璇ュ彧鐪嬪埌鍙嬪ソ鐨勬棤娉曡闂殑椤甸潰銆

Naturally, of course, developers need to see tracebacks to debug problems in their code. So the framework should hide all error messages from the public, but it should display them to the trusted site developers.

褰撶劧锛屽紑鍙戣呴渶瑕佸湪debug鏃剁湅鍒拌皟璇曚俊鎭傚洜姝わ紝妗嗘灦灏辫灏嗚繖浜涘嚭閿欐秷鎭樉绀虹粰鍙椾俊浠荤殑缃戠珯寮鍙戣咃紝鑰岃鍚戝叕浼楅殣钘忋

The Solution

瑙e喅鏂规

Django has a simple flag that controls the display of these error messages. If the DEBUG setting is set to True , error messages will be displayed in the browser. If not, Django will render return an HTTP 500 (Internal server error) message and render an error template that you provide. This error template is called 500.html and should live in the root of one of your template directories.

Django鏈変竴涓畝鍗曠殑鏍囧織绗︼紝鏉ユ帶鍒惰繖浜涘嚭閿欎俊鎭樉绀轰笌鍚︺傚鏋 DEBUG 琚缃负 True 锛岄敊璇秷鎭氨浼氭樉绀哄湪娴忚鍣ㄤ腑銆傚惁鍒欙紝Django浼氳繑鍥炰竴涓 HTTP 500锛堝唴閮ㄦ湇鍔″櫒閿欒锛夌殑娑堟伅锛 骞舵樉绀轰綘鎵鎻愪緵鐨勫嚭閿欓〉闈€傝繖涓敊璇殑妯℃澘鍙 500.html 锛屽苟涓旇繖涓枃浠堕渶瑕佷繚瀛樺湪浣犵殑鏌愪釜妯℃澘鐩綍鐨勬牴鐩綍涓

Because developers still need to see errors generated on a live site, any errors handled this way will send an email with the full traceback to any addresses given in the ADMINS setting.

鐢变簬寮鍙戣呬粛鐒堕渶瑕佸湪涓婄嚎鐨勭珯鐐逛笂鐪嬪埌鍑洪敊娑堟伅锛岃繖鏍风殑鍑洪敊淇℃伅浼氬悜 ADMINS 璁惧畾閫夐」鑷姩鍙戦乪mail銆

Users deploying under Apache and mod_python should also make sure they have PythonDebug Off in their Apache conf files; this will suppress any errors that occur before Django has had a chance to load.

鍦ˋpache鍜宮od_python涓嬪紑鍙戠殑浜哄憳锛岃繕瑕佷繚璇佸湪Apache鐨勯厤缃枃浠朵腑鍏抽棴 PythonDebug Off 閫夐」锛岃繖涓細鍦―jango琚姞杞戒互鍓嶅幓闄ゅ嚭閿欐秷鎭

A Final Word on Security

瀹夊叏棰嗗煙鐨勬荤粨

We hope all this talk of security problems isnt too intimidating. Its true that the Web can be a wild and wooly world, but with a little bit of foresight, you can have a secure Web site.

鎴戜滑甯屾湜鍏充簬瀹夊叏闂鐨勮璁猴紝涓嶄細澶浣犳劅鍒版亹鎱屻俉eb鏄竴涓澶勫竷婊¢櫡闃辩殑涓栫晫锛屼絾鏄彧瑕佹湁涓浜涜繙瑙侊紝浣犲氨鑳芥嫢鏈夊畨鍏ㄧ殑绔欑偣銆

Keep in mind that Web security is a constantly changing field; if youre reading the dead-tree version of this book, be sure to check more up to date security resources for any new vulnerabilities that have been discovered. In fact, its always a good idea to spend some time each week or month researching and keeping current on the state of Web application security. Its a small investment to make, but the protection youll get for your site and your users is priceless.

姘歌繙璁颁綇锛學eb瀹夊叏鏄竴涓笉鏂彂灞曠殑棰嗗煙銆傚鏋滀綘姝e湪闃呰杩欐湰涔︾殑鍋滄缁存姢鐨勯偅浜涚増鏈紝璇烽槄璇绘渶鏂扮増鏈殑杩欎釜閮ㄥ垎鏉ユ鏌ユ渶鏂板彂鐜扮殑婕忔礊銆備簨瀹炰笂锛屾瘡鍛ㄦ垨鑰呮瘡鏈堣姳鐐规椂闂存寲鎺榳eb搴旂敤瀹夊叏锛屽苟涓旇窡涓婃渶鏂扮殑鍔ㄦ佹槸涓涓緢濂界殑涓绘剰銆傚皬灏忕殑鎶曞叆锛屽嵈鑳芥敹鑾蜂繚鎶や綘鐨勭珯鐐瑰拰鐢ㄦ埛鐨勬棤浠风殑鍥炴姤銆

Whats Next

鎺ヤ笅鏉ワ紵

In the next chapter, well finally cover the subtleties of deploying Django: how to launch a production site and how to set it up for scalability.

涓嬩竴绔犱腑锛屾垜浠細璋堣鍒颁竴浜涗娇鐢―jango鐨勭粏鑺傞棶棰橈細濡備綍閮ㄧ讲涓涓珯鐐癸紝骞跺叿鏈夎壇濂界殑浼哥缉鎬с

Copyright 2006 Adrian Holovaty and Jacob Kaplan-Moss.
This work is licensed under the GNU Free Document License.
Hosting graciously provided by media temple
Chinese translate hosting by py3k.cn.