Skip to content

Commit bcfd17d

Browse files
authored
Merge pull request #6300 from freedomofpress/6291-tor2web-redirect
Updated tor2web check to return a 403 status on success
2 parents 9320c8c + 6353879 commit bcfd17d

File tree

4 files changed

+46
-58
lines changed

4 files changed

+46
-58
lines changed

securedrop/source_app/__init__.py

+12-24
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,7 @@
44
import os
55
import time
66
import werkzeug
7-
from flask import (Flask, render_template, escape, flash, Markup, request, g, session,
8-
url_for)
7+
from flask import (Flask, render_template, request, g, session, redirect, url_for)
98
from flask_babel import gettext
109
from flask_assets import Environment
1110
from flask_wtf.csrf import CSRFProtect, CSRFError
@@ -90,28 +89,8 @@ def handle_csrf_error(e: CSRFError) -> werkzeug.Response:
9089
for module in [main, info, api]:
9190
app.register_blueprint(module.make_blueprint(config)) # type: ignore
9291

93-
@app.before_request
94-
@ignore_static
95-
def check_tor2web() -> None:
96-
# ignore_static here so we only flash a single message warning
97-
# about Tor2Web, corresponding to the initial page load.
98-
if 'X-tor2web' in request.headers:
99-
flash(
100-
Markup(
101-
'<strong>{}</strong>&nbsp;{}&nbsp;<a href="{}">{}</a>'.format(
102-
escape(gettext("WARNING:")),
103-
escape(
104-
gettext(
105-
'You appear to be using Tor2Web, which does not provide anonymity.'
106-
)
107-
),
108-
url_for('info.tor2web_warning'),
109-
escape(gettext('Why is this dangerous?')),
110-
)
111-
),
112-
"banner-warning"
113-
)
114-
92+
# before_request hooks are executed in order of declaration, so set up g object
93+
# before the potential tor2web 403 response.
11594
@app.before_request
11695
@ignore_static
11796
def setup_g() -> Optional[werkzeug.Response]:
@@ -128,6 +107,15 @@ def setup_g() -> Optional[werkzeug.Response]:
128107

129108
return None
130109

110+
@app.before_request
111+
@ignore_static
112+
def check_tor2web() -> Optional[werkzeug.Response]:
113+
# TODO: expand header checking logic to catch modern tor2web proxies
114+
if 'X-tor2web' in request.headers:
115+
if request.path != url_for('info.tor2web_warning'):
116+
return redirect(url_for('info.tor2web_warning'))
117+
return None
118+
131119
@app.errorhandler(404)
132120
def page_not_found(error: werkzeug.exceptions.HTTPException) -> Tuple[str, int]:
133121
return render_template('notfound.html'), 404

securedrop/source_app/info.py

+8-3
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,25 @@
11
# -*- coding: utf-8 -*-
22
import flask
3-
from flask import Blueprint, render_template, send_file, redirect, url_for
3+
from flask import Blueprint, render_template, send_file, redirect, url_for, flash
4+
from flask_babel import gettext
45
import werkzeug
56

67
from io import BytesIO # noqa
78

89
from encryption import EncryptionManager
910
from sdconfig import SDConfig
11+
from source_app.utils import get_sourcev3_url
1012

1113

1214
def make_blueprint(config: SDConfig) -> Blueprint:
1315
view = Blueprint('info', __name__)
1416

1517
@view.route('/tor2web-warning')
16-
def tor2web_warning() -> str:
17-
return render_template("tor2web-warning.html")
18+
def tor2web_warning() -> flask.Response:
19+
flash(gettext("Your connection is not anonymous right now!"), "error")
20+
return flask.Response(
21+
render_template("tor2web-warning.html", source_url=get_sourcev3_url()),
22+
403)
1823

1924
@view.route('/use-tor')
2025
def recommend_tor_browser() -> str:
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,18 @@
11
{% extends "base.html" %}
22
{% block body %}
3-
<h1>{{ gettext('Why is there a warning about Tor2Web?') }}</h1>
4-
<p>{{ gettext('Using Tor2Web to connect to SecureDrop will not protect your anonymity.') }}</p>
5-
<p>{{ gettext('It could be possible for anyone monitoring your Internet traffic (your government, your Internet provider), to identify you.') }}
3+
<h1>{{ gettext('Proxy Service Detected') }}</h1>
4+
<div class="center">
5+
{% include 'flashed.html' %}
6+
</div>
7+
<p>{{ gettext('You appear to be using a Tor proxy service to access SecureDrop. Proxy services do not protect your anonymity.') }}
68
</p>
7-
<p>{{ gettext('We <strong>strongly advise</strong> you to use the <a href="www.torproject.org/projects/torbrowser.html">Tor Browser</a> instead.') }}
9+
<p>{{ gettext('Anyone monitoring your Internet traffic &mdash; including your government, your Internet provider, or the proxy operator &mdash; may be able to identify you.') }}
810
</p>
9-
{% endblock %}
11+
<p>{{ gettext('Always use <a href="www.torproject.org/projects/torbrowser.html">Tor Browser</a> to access SecureDrop. Valid SecureDrop site addresses end with <code>.onion</code>. The correct address for this site is:') }}
12+
<pre>{{ source_url }}</pre>
13+
</p>
14+
<p> {{ gettext ('If you are already using Tor Browser, copy the address above and generate a new identity before you access SecureDrop.') }} {{ gettext('Click the <img src={icon} alt="" width="16" height="16">&nbsp;<b>New Identity</b> button in your Tor Browser\'s toolbar. This will clear your Tor Browser activity data on this device.').format(icon=url_for('static', filename='i/torbroom-black.png')) }}
15+
</p>
16+
<p>{{ gettext('If there is a reasonable risk of your Internet traffic being monitored, consider connecting from a different location or network.') }}
17+
</p>
18+
{% endblock %}

securedrop/tests/test_source.py

+12-26
Original file line numberDiff line numberDiff line change
@@ -537,38 +537,24 @@ def test_submit_sanitizes_filename(source_app):
537537
mtime=0)
538538

539539

540-
@flaky(rerun_filter=utils.flaky_filter_xfail)
541-
@pytest.mark.parametrize("locale", get_test_locales())
542-
def test_tor2web_warning_headers(config, source_app, locale):
540+
@pytest.mark.parametrize("test_url", ['main.index', 'main.create', 'main.submit'])
541+
def test_redirect_when_tor2web(config, source_app, test_url):
543542
with source_app.test_client() as app:
544-
with InstrumentedApp(app) as ins:
545-
resp = app.get(url_for('main.index', l=locale), headers=[('X-tor2web', 'encrypted')])
546-
assert resp.status_code == 200
547-
548-
assert page_language(resp.data) == language_tag(locale)
549-
msgids = [
550-
"WARNING:",
551-
"You appear to be using Tor2Web, which does not provide anonymity.",
552-
"Why is this dangerous?",
553-
]
554-
with xfail_untranslated_messages(config, locale, msgids):
555-
ins.assert_message_flashed(
556-
'<strong>{}</strong>&nbsp;{}&nbsp;<a href="{}">{}</a>'.format(
557-
escape(gettext(msgids[0])),
558-
escape(gettext(msgids[1])),
559-
url_for('info.tor2web_warning'),
560-
escape(gettext(msgids[2])),
561-
),
562-
'banner-warning'
563-
)
564-
543+
resp = app.get(
544+
url_for(test_url),
545+
headers=[('X-tor2web', 'encrypted')],
546+
follow_redirects=True)
547+
text = resp.data.decode('utf-8')
548+
assert resp.status_code == 403
549+
assert "Proxy Service Detected" in text
565550

566551
def test_tor2web_warning(source_app):
567552
with source_app.test_client() as app:
568553
resp = app.get(url_for('info.tor2web_warning'))
569-
assert resp.status_code == 200
554+
assert resp.status_code == 403
570555
text = resp.data.decode('utf-8')
571-
assert "Why is there a warning about Tor2Web?" in text
556+
assert "Proxy Service Detected" in text
557+
572558

573559

574560
def test_why_use_tor_browser(source_app):

0 commit comments

Comments
 (0)