有很多關于如何使用flask_session 實作服務器端存盤的教程/示例,但我無法弄清楚如何防止客戶端訪問會話。想象一下,我想存盤與客戶端相關的資料,但該客戶端無法訪問資料。我可以將它存盤在服務器端會話中以在頁面訪問中保持不變,但我注意到我可以通過 jinja 從客戶端輕松訪問它:{{session}}
{{session}} 將整個會話列印給用戶 - 有沒有辦法防止這種情況?或者將某些變數設為“私有”?也許會話是完全錯誤的方法?
對此相當陌生,因此任何建議都會很棒。
uj5u.com熱心網友回復:
TL;DR:你做的一切都是正確的,你所描述的不是一個漏洞。
默認情況下,您使用 Flask 使用服務器端渲染,這意味著您的程式在將 HTML 發送到用戶的 Web 瀏覽器之前將其完整地組合起來。為此,它使用模板引擎,在 Flask 中最常見的是 Jinja。
因為頁面可能希望在其上具有特定于用戶的內容,所以它們應該能夠訪問會話物件是有道理的。但是用戶無法看到模板代碼或提供給模板的引數——他們只能看到最終的、“渲染的”HTML,所有的替換都已經完成。您可以在模板中使用會話物件這一事實并不是安全漏洞 —— 也就是說,按照設計,會話物件的功能之一。
那么你怎么知道客戶端是否可以看到你的會話呢?你測驗一下!具體來說,我們將有一個簡單的應用程式來記錄您的姓名和 PIN 碼,它應該只向您回顯您 PIN 碼的最后一位數字,并查看客戶端 cookie 中的可用資料。(這僅用于演示目的——有關正確秘密存盤的示例,請查看其他地方。)
templates/index.html:
<html>
<body>
{% if 'name' in session %}
<p>Your name is: {{session['name']}}</p>
{% else %}
<p>Your name is unset.</p>
{% endif %}
{% if 'pin' in session %}
<p>Your PIN ends in: {{session['pin'][-1]}}</p>
{% else %}
<p>Your PIN is unset.</p>
{% endif %}
</body>
</html>
test1.py:
from flask import Flask, session, render_template
app = Flask(__name__)
app.config['SECRET_KEY'] = b'notverysecret'
@app.route('/')
def get_name():
return render_template('index.html')
@app.route('/set/<name>/<pin>')
def set_name(name, pin):
session['name'] = name
session['pin'] = pin
return f'Your name has been set to {name} and your PIN now ends with {pin[-1]}.\n'
if __name__ == '__main__':
app.run('127.0.0.1', 5000)
讓我們運行服務器程式并使用它curl向服務器發出請求。這些-b cookies.txt -c cookies.txt標志意味著我們會將存盤在cookies.txt我們請求中的 cookie 發送到服務器,并將我們回傳的 cookie 保存在同一個檔案中——就像網路瀏覽器如何使用它們一樣。
$ curl -b cookies.txt -c cookies.txt http://localhost:5000/set/danya02/1234
Your name has been set to danya02 and your PIN now ends with 4.
$ curl -b cookies.txt -c cookies.txt http://localhost:5000/
<html>
<body>
<p>Your name is: danya02</p>
<p>Your PIN ends in: 4</p>
</body>
</html>
$ cat cookies.txt
# Netscape HTTP Cookie File
# https://curl.se/docs/http-cookies.html
# This file was generated by libcurl! Edit at your own risk.
#HttpOnly_localhost FALSE / FALSE 0 session eyJuYW1lIjoiZGFueWEwMiIsInBpbiI6IjEyMzQifQ.YZiywg.EnQTwefXVM33JguMPHmST-WK1bU
服務器為我們提供了 cookie,我們可以使用它稍后檢索 PIN 的名稱和最后一位數字,但該 cookie 包含什么?它包含 string eyJuYW1lIjoiZGFueWEwMiIsInBpbiI6IjEyMzQifQ.YZiywg.EnQTwefXVM33JguMPHmST-WK1bU,它是由點分隔的三個Base64字串。如果您將其中的第一個轉換為文本...
$ base64 -d
eyJuYW1lIjoiZGFueWEwMiIsInBpbiI6IjEyMzQifQ
{"name":"danya02","pin":"1234"}base64: invalid input
...then, despite the complaint from the base64 program, we can recover the data in the cookie. (The other two parts in the cookie are a cryptographic signature that ensures that the client can't tamper with the values stored in it without the server noticing.)
This is exactly what you're trying to avoid -- the client must not be able to see this information. And you're correct in that the flask_session library is the way to do it. So, let's edit the server program to use it instead of the default session object. Luckily, because of how that library was built, it requires very little change in our code:
test2.py:
from flask import Flask, session, render_template
from flask_session import Session # <-- added
app = Flask(__name__)
SESSION_TYPE = 'filesystem' # <-- added
app.config.from_object(__name__) # <-- added
Session(app) # <-- added
@app.route('/')
def get_name():
return render_template('index.html')
@app.route('/set/<name>/<pin>')
def set_name(name, pin):
session['name'] = name
session['pin'] = pin
return f'Your name has been set to {name} and your PIN now ends with {pin[-1]}.\n'
if __name__ == '__main__':
app.run('127.0.0.1', 5000)
Let's now do the same test with this new server program:
$ rm cookies.txt
$ curl -b cookies.txt -c cookies.txt http://localhost:5000/set/danya02/1234
Your name has been set to danya02 and your PIN now ends with 4.
$ curl -b cookies.txt -c cookies.txt http://localhost:5000/
<html>
<body>
<p>Your name is: danya02</p>
<p>Your PIN ends in: 4</p>
</body>
</html>
$ cat cookies.txt
# Netscape HTTP Cookie File
# https://curl.se/docs/http-cookies.html
# This file was generated by libcurl! Edit at your own risk.
#HttpOnly_localhost FALSE / FALSE 1640076681 session 0222c14c-b34b-4467-87ec-7618f8110766
這一次,我們的 cookie 中有一些不同的東西——某種UUID。這給我們沒有關于我們寫入會話的資訊,因為 UUID 和實際變數之間的映射存盤在服務器上(在這種情況下,在檔案flask_session夾中的某個檔案中,盡管可以更改 - 請參閱有關更多詳細資訊,請參閱庫的配置選項)。
簡而言之,這意味著我們的會話確實存盤在服務器上,客戶端無法訪問它。但是服務器仍然可以訪問它,特別是模板,這沒關系,因為模板只顯示客戶端應該看到的資訊。因此,只要您不決定泄露會話物件——比如說,通過{{session}}在模板中使用一個空物件——那么您的會話資訊只能由服務器訪問,這正是您想要的。
但是因為會話物件很容易泄露,所以你不應該用它來存盤真正敏感的資訊,即使它只對服務器可見。對于重要資訊,您需要某種存盤方式,您必須明確選擇在其中執行的每個操作(例如讀取、寫入、創建或洗掉)。為此,您需要一個具有某種描述的資料庫,并且有很多關于如何做到這一點的資源——您可以從Flask 教程應用程式開始,它將它用于用戶提交的資料。
轉載請註明出處,本文鏈接:https://www.uj5u.com/qukuanlian/362698.html
