จุดอ่อนด้านความปลอดภัยของแอปพลิเคชันเว็บที่พบบ่อยที่สุดคือความล้มเหลวในการตรวจสอบการป้อนข้อมูลอย่างถูกต้องจากไคลเอนต์หรือสภาพแวดล้อม
OWASP, การตรวจสอบข้อมูล (2 กรกฎาคม 2017)
ความล้มเหลวในการจัดการอินพุตที่ไม่น่าเชื่อถืออย่างถูกต้องทำให้แอปพลิเคชันมีความเสี่ยงต่อการโจมตีการฉีดรหัสเช่นการเขียนสคริปต์ไซต์ข้าม (XSS) "รูปแบบของความผิดพลาดที่มองเห็นได้จากการป้อนข้อมูลจากลูกค้าหรือแหล่งที่ไม่น่าเชื่อถือ" เป็นมาตรการตอบโต้หนึ่งครั้งที่ Owasp แนะนำซึ่งเป็นส่วนหนึ่งของกลยุทธ์การป้องกันในเชิงลึก
โมดูล Python นี้ "taints" อินพุตที่ไม่น่าเชื่อถือโดยการห่อเป็นพิเศษ ประเภทเหล่านี้มีพฤติกรรมในทุกแง่มุมเช่นเดียวกับประเภทงูหลามดั้งเดิม แต่ป้องกันไม่ให้คุณใช้ค่าของพวกเขาโดยไม่ตั้งใจ สิ่งนี้ไม่เพียงแค่ "มองเห็นได้ชัดเจน" แต่การรับประกันรันไทม์และ (ทางเลือก) ความปลอดภัยประเภทที่สามารถตรวจสอบได้ด้วย MyPy
กลยุทธ์สำหรับการฆ่าเชื้อ, การหลบหนี, การทำให้เป็นมาตรฐานหรือการตรวจสอบความถูกต้องของอินพุตนั้นอยู่นอกขอบเขตสำหรับโมดูลนี้
โมดูลนี้ควรเหมาะสำหรับการใช้งานการผลิตพร้อมคำเตือนต่อไปนี้:
untrusted.sequence ประเภท ลำดับ คือการทดสอบที่ขาดหายไปและอาจไม่สมบูรณ์untrusted.mapping ประเภทการทำแผนที่ขาดหายไปและอาจไม่สมบูรณ์unstrusted.<*>Of ไม่ได้ทดสอบอย่างเต็มที่รหัสใด ๆ ที่มีข้อควรพิจารณาด้านความปลอดภัยสมควรได้รับมาตรฐานสูงสุดของการตรวจสอบข้อเท็จจริง โปรดใช้วิจารณญาณของคุณก่อนใช้โมดูลนี้
ซอฟต์แวร์มีให้ "ตามสภาพ" โดยไม่มีการรับประกันใด ๆ
ทดสอบ Python> = 3.4 แต่รุ่นก่อนหน้านี้อาจใช้งานได้
sudo pip3 install untrusted --upgrade
(หากคุณติดตั้ง Python3 เท่านั้นคุณอาจต้องใช้ pip เท่านั้น - ไม่ใช่ pip3 )
import html # for html.escape
import untrusted
line = untrusted . string ( input ( "Enter some text, HTML will be escaped: " ))
try :
# You're not allowed to print an untrusted.string!
# This raises a TypeError!
print ( "<b>You typed:</b> " + line )
except TypeError :
pass # Expected
# Safely escape the HTML!
print ( "<b>You typed:</b> " + line / html . escape )
# / is overloaded as shorthand for:
# print("<b>You typed:</b> " + line.escape(html.escape)) untrusted.stringstr แต่ไม่สามารถส่งออกได้โดยไม่ต้องหลบหนีstr แบบดั้งเดิมstr พื้นเมืองและ untrusted.string taints ค่า str /ส่งเสริมให้พวกเขาเป็นประเภท untrusted.stringตัวอย่างการจัดการ HTML ที่ไม่น่าเชื่อถือ:
import html # for html.escape()
import untrusted
# example of a string that could be provided by an untrusted user
firstName = untrusted . string ( "Grace" )
lastName = untrusted . string ( "<script>alert('hack attempt!');</script>" )
# works seamlessly with native python strings
fullName = firstName + " " + lastName
# fullName was tainted and keeps the untrusted.string type
print ( repr ( fullName )) # <untrusted.string of length 46>
# the normal string methods still work as you would expect
fullName = fullName . title ()
# but you can't accidentally use it where a `str` is expected
try :
print ( fullName ) # raises TypeError - untrusted.string used as a `str`!
fullName . encode ( 'utf8' ) # also raises TypeError
except TypeError :
print ( "We caught an error, as expected!" )
# instead, you are forced to explicitly escape your string somehow
print ( "<b>Escaped output:</b> " + fullName . escape ( html . escape )) # prints safe HTML!
print ( "<b>Escaped output:</b> " + fullName / html . escape ) # use this easy shorthand! ดู untrustedStringExample.py สำหรับการเขียนฟังก์ชั่นการหลบหนีหลายรายการผ่านอาร์กิวเมนต์เพื่อหลบหนีฟังก์ชั่น ฯลฯ
โมดูลนี้จัดเตรียมประเภทให้กับคอลเลกชันที่ไม่น่าเชื่อถือของค่าที่ไม่น่าเชื่อถือ ค่าถูกห่อด้วยประเภท untrusted.* พิมพ์เมื่อเข้าถึง
untrusted.iteratoruntrusted.* ประเภทuntrusted.string สเตรต แต่ก็สามารถเป็นตัววนซ้ำได้มากกว่าคอลเลกชันที่ไม่น่าเชื่อถือตัวอย่าง:
import html # for html.escape
import untrusted
it = untrusted . iterator ( open ( "example.txt" ))
for i , s in enumerate ( it ):
print ( "Line %d: %s" % ( i , s . escape ( html . escape ). strip ())) ดู examples/untrustedIteratorExample.py สำหรับรายการซ้อนที่ไม่น่าเชื่อถือ (เช่น untrusted.iterator ของ untrusted.iterator ของ untrusted.string )
untrusted.sequencelist ใด ๆ ที่มีค่าที่ไม่น่าเชื่อถือuntrusted.* ประเภทuntrusted.string สเตรต แต่ก็สามารถเป็นตัววนซ้ำได้มากกว่าคอลเลกชันที่ไม่น่าเชื่อถือตัวอย่าง:
import html # for html.escape
import untrusted
# list of strings from an untrusted source
animals = [ "cat" , "dog" , "monkey" , "elephant" , "<big>mouse</big>" ]
untrustedAnimalList = untrusted . sequence ( animals )
assert "cat" in untrustedAnimalList untrusted.mappingdict -like ใด ๆ ที่เชื่อถือได้หรือไม่น่าเชื่อถือสำหรับค่าที่ไม่น่าเชื่อถือuntrusted.*str -> untrusted.string แต่ก็อาจเป็นการทำแผนที่ unstrusted.string -> untrusted.string หรือการแมปกับคอลเลกชันที่ไม่น่าเชื่อถือตัวอย่าง:
import html # for html.escape
import untrusted
user = untrusted . mapping ({
'username' : 'example-username<b>hack attempt</b>' ,
'password' : 'example-password' ,
})
try :
print ( user . get ( "username" ))
except TypeError :
print ( "Caught the error we expected!" )
print ( user . get ( "username" , "default-username" ) / html . escape )ตัวอย่าง:
import html # for html.escape
import untrusted
untrustedType = untrusted . mappingOf ( untrusted . string , untrusted . string )
args = untrustedType ({ 'animal' : 'cat' , '<b>hack</b>' : 'attempt' })
for k , v in args . items ():
print ( "%s: %s" % ( k / html . escape , v / html . escape ))คอนเทนเนอร์ซ้อนที่เกียจคร้านได้รับการสนับสนุนอย่างเต็มที่เช่นกัน
ใช้ untrusted.iteratorOf(valueType) , untrusted.sequenceOf(valueType) หรือ untrusted.mappingOf(keyType, valueType) เพื่อสร้างประเภทคอนเทนเนอร์เฉพาะ
ตัวอย่าง:
import html # for html.escape
import untrusted
people = [
{
'id' : 'A101' ,
'name.first' : 'Grace' ,
'name.last' : 'Hopper' ,
'name.other' : 'Brewster Murray' ,
'dob' : '1906-12-09' ,
},
{
'id' : 'A102' ,
'name.first' : 'Alan' ,
'name.last' : 'Turing' ,
'name.other' : 'Mathison' ,
'dob' : '1912-06-23' ,
},
{
'id' : 'HACKER' ,
'name.first' : 'Robert ' ); DROP TABLE Students;--' ,
'name.last' : '£Hacker' ,
'dob' : '<b>Potato</b>'
},
]
# a list of dicts with trusted keys, but untrusted values
mappingType = untrusted . sequenceOf ( untrusted . mapping )
# aka (setting defaults explicitly)
mappingType = untrusted . sequenceOf ( untrusted . mappingOf ( str , untrusted . string ))
for person in mappingType ( people ):
for key , value in person . items ():
print ( " %s: %s" % ( key , value . escape ( html . escape ))) รองรับวิธี str ดั้งเดิมส่วนใหญ่ แต่อาจมีประเภทผลตอบแทนที่แตกต่างกัน
ค่าพื้นฐาน - มักจะเป็นอินสแตนซ์ที่ไม่มีไม่มีของ str
untrusted.string(s)
สร้างวัตถุ untrusted.string โดยที่ s เป็นอินสแตนซ์ที่ไม่มีไม่มีของ str หรือ untrusted.string
ในกรณีของ untrusted.string การสร้างด้วยอาร์กิวเมนต์ untrusted.string สตริปค่าใหม่จะถูกห่อหุ้มเพียงครั้งเดียว การหลบหนีมันจะให้ str ไม่ใช่ที่ untrusted.string
untrusted.string.escape(escape_function, [*args, **kwargs]) -> str
ใช้ escape_function ฟังก์ชั่น str -> str ที่หลบหนีสตริงและส่งคืนด้วยอาร์กิวเมนต์เสริมและอาร์กิวเมนต์คำหลัก
untrusted.string.valid(valid_function, [*args, **kwargs]) -> bool
ใช้ valid_function , ฟังก์ชัน str -> bool ที่ตรวจสอบสตริงและส่งคืนจริงหรือเท็จพร้อมอาร์กิวเมนต์ที่เป็นตัวเลือกและอาร์กิวเมนต์คำหลัก
untrusted.string.valid(validate_function, [*args, **kwargs]) -> any
ใช้ valid_function , ฟังก์ชัน str -> any , ที่ตรวจสอบสตริงและส่งคืนค่าใด ๆ (เช่นรายการเหตุผลว่าทำไมสตริงไม่ได้ตรวจสอบ) พร้อมอาร์กิวเมนต์เสริมและอาร์กิวเมนต์คำหลัก
untrusted.string / escape_expr สำหรับนิพจน์การหลบหนีบางอย่าง untrusted.string("value") / escape_expr -> str
escape_expr ที่นี่เป็นทั้งฟังก์ชัน str -> str ที่หลบหนีสตริงหรือ 3 -tuple (escape_function, args, kwargs_dict) ตัวอย่างเช่น:
import html # for html.escape
import untrusted
myString = untrusted . string ( "<b>Exam " ple</b>" )
myEscape = ( html . escape , [], { 'quote' : False })
print ( myString / html . escape )
print ( myString / myEscape ) โดยที่ collection เป็นหนึ่งใน iterator sequence (เหมือนรายการ) mapping (เหมือนคำสั่ง) หรือประเภทที่กำหนดเองที่ผู้ใช้สร้างขึ้น
รองรับวิธีการรวบรวมดั้งเดิมส่วนใหญ่ แต่อาจมีประเภทผลตอบแทนที่แตกต่างกัน
แต่ละคอลเลกชันจะรับรู้ถึง ประเภทค่า ของมัน ( untrusted.string )
การแมปยังตระหนักถึง ประเภทคีย์ ของพวกเขา (Strapt str )
ค่าพื้นฐาน - มักจะเป็นวัตถุที่ไม่ใช่ไม่มีที่เป็นอินสแตนซ์ของประเภทค่าของคอลเลกชัน
สร้างประเภทตัววนซ้ำโดยใช้ untrusted.iteratorOf(type)
สร้างประเภทลำดับโดยใช้ untrusted.sequenceOf(type)
สร้างประเภทการแมปโดยใช้ untrusted.sequenceOf(keyType, valueType)
คำจำกัดความเหล่านี้เรียกซ้ำ
ตัวอย่างเช่น:
import untrusted
itType = untrusted . iteratorOf ( untrusted . iteratorOf ( untrusted . string ))
seqType = untrusted . sequenceOf ( itType )
mappingType = untrusted . mappingOf ( untrusted . string , seqType )
someDict = {}
myMapping = mappingType ( someDict )
# or, as a less readable one-liner
myMapping = untrusted . mappingOf ( untrusted . string , seqType )( someDict ) โปรดจำไว้ว่าเพียงเพราะคุณใช้วิธีหนึ่งในการหลบหนี untrusted.string สตีลเข้าไปใน str มันอาจไม่ปลอดภัยในบริบทอื่น ๆ ตัวอย่างเช่นสิ่งที่ปลอดภัยสำหรับ HTML อาจยังคงเป็นอันตราย SQL ในเวลานั้นการป้อนข้อมูลผู้ใช้จะไม่เป็นที่รู้จักล่วงหน้าบริบทที่จะใช้ - และดังนั้นจึงยังไม่ทราบว่าการตรวจสอบความถูกต้องและกลยุทธ์การหลบหนีที่ถูกต้องคืออะไร เป็นการดีที่สุดที่จะชะลอการหลบหนีจนกว่าจะถึงจุดสุดท้ายของการใช้งาน - เก็บค่าไว้โดย untrusted.* ให้นานที่สุด
โมดูลนี้ไม่ใช่วิธีแก้ปัญหาวิเศษ เป็นเครื่องมือที่จะใช้อย่างชาญฉลาด อย่าตกหลุมพรางของการคิดเกี่ยวกับคุณค่า "ตอนนี้เป็น str ดังนั้นมันจึงปลอดภัยอย่างสมบูรณ์"
เพียงเพราะสตริงสามารถหลบหนีได้อย่างปลอดภัยไม่ได้หมายความว่าได้รับการตรวจสอบแล้วว่าเป็นอินพุตที่อนุญาต ตัวอย่างเช่นคุณอาจใส่ขีด จำกัด ขั้นต่ำบนรหัสผ่าน หรือคุณอาจต้องการให้อินพุตไม่ใช่ชื่อไฟล์ที่สงวนไว้
วิธีที่ดีในการทำเช่นนี้คือการใช้วิธีการ unstruted.string.valid ซึ่งส่งคืนวิธีบูลีน untrusted.string.validate วิธีการ Validate ซึ่งส่งคืน ValueError ใด ๆ (เช่นนี่อาจเป็นรายการของเหตุผลว่าทำไมอินพุตไม่ได้เป็น valdiate)
บางครั้งคุณสร้าง HTML ด้วยตัวเองและการหลบหนีเป็นสิ่งที่คุณต้องทำ
บางครั้งอินเทอร์เฟซจะแยกพารามิเตอร์ออกจากรหัสเช่นไลบรารี SQL ที่ทันสมัยที่สุด เสมอ ในกรณีนี้ไดรเวอร์ฐานข้อมูลจะจัดการอินพุตที่ไม่น่าเชื่อถือที่อาจไม่น่าเชื่อถือกว่าที่คุณคาดหวังและหลบหนีเป็นสิ่งที่ผิดที่จะทำ
คุณอาจทำเครื่องหมายอินพุตที่ไม่น่าเชื่อถือด้วยเหตุผลอื่น - เช่นเพื่อบังคับใช้ความยาวสูงสุดในการค้นหาหรือเพราะคุณต้องตรวจสอบความถูกต้องกับตรรกะทางธุรกิจ
ประเภทคอลเลกชันที่ไม่น่าเชื่อถือเช่น untrusted.sequence คือ "มุมมอง" เหนือวัตถุอันเดอร์ลิง หากวัตถุพื้นฐานเปลี่ยนแปลงวัตถุที่คุณเห็นผ่านประเภทคอลเลกชันที่ไม่น่าเชื่อถือ กล่าวอีกนัยหนึ่ง: เป็นการอ้างอิงถึงวัตถุเดียวกันไม่ใช่สำเนา หากนั่นไม่ใช่สิ่งที่คุณต้องการให้ใช้โมดูลคัดลอก
หวังว่าสิ่งนี้ควรจะเป็นพฤติกรรมที่ชัดเจนและไม่ได้รับการสนับสนุนไม่ได้เป็นเอกลักษณ์ของโมดูลนี้ แต่มันสามารถเดินทางคนได้
นี่คือซอฟต์แวร์ฟรี (ใบอนุญาต MIT)
untrusted - write safer Python with special untrusted types
Copyright © 2017 - 2018 Ben Golightly <[email protected]>
Copyright © 2017 - 2018 Tawesoft Ltd <[email protected]>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.