Skip to content

Commit c0eff63

Browse files
authored
Merge pull request #28 from DefangLabs/linda-csrf-protection
Added CSRF protection and rate-limiting
2 parents cd843da + 2711a0c commit c0eff63

File tree

6 files changed

+39
-2
lines changed

6 files changed

+39
-2
lines changed

.github/workflows/deploy.yaml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,10 @@ jobs:
3434
- name: Deploy
3535
uses: DefangLabs/[email protected]
3636
with:
37-
config-env-vars: OPENAI_API_KEY
37+
config-env-vars: OPENAI_API_KEY SECRET_KEY
3838
mode: production
3939
provider: aws
4040

4141
env:
4242
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
43+
SECRET_KEY: ${{ secrets.SECRET_KEY }}

app/app.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,17 @@
11
from flask import Flask, request, jsonify, render_template, Response, stream_with_context
2+
from flask_wtf.csrf import CSRFProtect
23
from rag_system import rag_system
34
import subprocess
45
app = Flask(__name__, static_folder='templates/images')
56

7+
import os
8+
9+
app.config['SECRET_KEY'] = os.getenv('SECRET_KEY')
10+
app.config['SESSION_COOKIE_HTTPONLY'] = True
11+
app.config['SESSION_COOKIE_SECURE'] = bool(os.getenv('SESSION_COOKIE_SECURE'))
12+
13+
csrf = CSRFProtect(app)
14+
615
@app.route('/', methods=['GET', 'POST'])
716
def index():
817
return render_template('index.html')

app/requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
Flask==2.0.1
2+
Flask-WTF==1.2.2
23
Werkzeug==2.0.3
34
scikit-learn==0.24.2
45
numpy==1.22.0

app/templates/index.html

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,27 @@ <h2>Ask Defang</h2>
207207
}
208208
});
209209

210+
async function rateLimitingFetch(url, options = {}) {
211+
if (window.crypto && window.crypto.subtle) {
212+
let bodyHash, nonceArray = new Uint32Array(1), nonce = 0;
213+
const body = new TextEncoder().encode(options.body);
214+
const bodyWithNonce = new Uint8Array(nonceArray.byteLength + body.byteLength);
215+
bodyWithNonce.set(body, nonceArray.byteLength);
216+
217+
do {
218+
nonceArray[0] = ++nonce;
219+
bodyWithNonce.set(new Uint8Array(nonceArray.buffer));
220+
bodyHash = await crypto.subtle.digest('SHA-256', bodyWithNonce);
221+
} while(new DataView(bodyHash).getUint32(0) > 0x50000);
222+
223+
options.headers = {
224+
...options.headers,
225+
'X-Nonce': nonce
226+
}
227+
return fetch(url, options);
228+
}
229+
}
230+
210231
function sendQuery() {
211232
const query = queryInput.value.trim();
212233
if (query === '') return;
@@ -228,10 +249,11 @@ <h2>Ask Defang</h2>
228249
sendButton.disabled = true;
229250

230251
// Send query to server
231-
fetch('/ask', {
252+
rateLimitingFetch('/ask', {
232253
method: 'POST',
233254
headers: {
234255
'Content-Type': 'application/json',
256+
'X-CSRFToken': '{{ csrf_token() }}'
235257
},
236258
body: JSON.stringify({ query: query }),
237259
})

compose.dev.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ services:
1010
mode: ingress
1111
environment:
1212
FLASK_APP: app.py
13+
SECRET_KEY: supersecret
14+
SESSION_COOKIE_SECURE: 0
1315
OPENAI_API_KEY: ${OPENAI_API_KEY} # Set your OpenAI API key here or in the .env file
1416
command: flask run --host=0.0.0.0
1517
deploy:

compose.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ services:
1313
mode: ingress
1414
environment:
1515
FLASK_APP: app.py
16+
SECRET_KEY:
17+
SESSION_COOKIE_SECURE: 1
1618
OPENAI_API_KEY: ${OPENAI_API_KEY} # Set your OpenAI API key here or in the .env file
1719
command: uwsgi --http 0.0.0.0:5000 --wsgi-file app.py --callable app --processes 4 --threads 2
1820
deploy:

0 commit comments

Comments
 (0)