Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Possibility of Open Redirect #13

Merged
merged 8 commits into from
Sep 6, 2018
Merged

Possibility of Open Redirect #13

merged 8 commits into from
Sep 6, 2018

Conversation

mtb-beta
Copy link
Contributor

@mtb-beta mtb-beta commented Aug 29, 2018

オープンリダイレクトの可能性があったので修正しました。取り込みをお願いします。
以下の修正が含まれています。

  • logout viewのopen redirect脆弱性の修正
  • login viewのredirectのユニットテストに問題があったので合わせて修正/追加
  • login viewのユニットテストでdeprecatedなメソッドを使っていたので修正

@mtb-beta mtb-beta changed the title [WIP] オープンリダイレクトの可能性 [WIP] Possibility of Open Redirect Aug 29, 2018
@@ -72,7 +73,7 @@ def logout(request, next_page=None, template_name='registration/logged_out.html'
redirect_to = request.POST.get(redirect_field_name, getattr(settings, 'LOGOUT_REDIRECT_URL', ''))
else:
redirect_to = request.GET.get(redirect_field_name, getattr(settings, 'LOGOUT_REDIRECT_URL', ''))
if redirect_to:
if redirect_to and is_safe_url(redirect_to):
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • ログアウト時のリダイレクト先が安全かを確認します。(ログアウト時のリダイレクトは、URLクエリで受け取れるため信用できない)

@@ -28,7 +30,7 @@ def test_login(self):
'password': 'password',
})
self.assertEquals(response.status_code, 302)
self.assert_(response['Location'].endswith(settings.LOGIN_REDIRECT_URL))
self.assertTrue(response['Location'].endswith(settings.LOGIN_REDIRECT_URL))
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • 非推奨のメソッドを使っていたので推奨のメソッドに差し替え。

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

@@ -58,41 +60,122 @@ def test_fail_login_blank_fields(self):
def test_bad_redirect_space(self):
bad_next_url = 'test space url'
self.assertContains(self.client.get('/account/login/'), '<form')
response = self.client.post('/account/login/?%s=%s' % (REDIRECT_FIELD_NAME, quote(bad_next_url)), {
response = self.client.post('/account/login/', {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

self.assertTrue(response['Location'].endswith(ok_url))

@pytest.mark.django_db
class LogoutViewsTest(DjangoTestCase):
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • ログアウト時のOpenRedirect用ユニットテストを追加するついでに、通常のlogout viewのテストも追加

import pytest
from django.utils.six.moves.urllib_parse import quote
from django.test import TestCase as DjangoTestCase
from django.contrib.auth import REDIRECT_FIELD_NAME
from django.conf import settings
from django.http import HttpResponse
from django import shortcuts

from testapp.models import TestBasicUser


@pytest.mark.django_db
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DjangoTestCase使ってるならこのmarkはいらいないのでは

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

いらなかったので消しました。

@mtb-beta mtb-beta changed the title [WIP] Possibility of Open Redirect Possibility of Open Redirect Aug 29, 2018
@mtb-beta
Copy link
Contributor Author

どなたかレビューお願いします!

self.assertEquals(response.status_code, 302)
self.assertTrue(response['Location'].endswith('/some/url?param=http://example.com/'))

def test_ok_redirect(self):
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • 正常系がなかったため、FIXME:のテストの切り分けが難しくなったと思われた。
  • 正常系のテストケースを追加した。

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

Copy link
Member

@tell-k tell-k left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

軽く見れる範囲でレビューしました

fixtures = ['authutils_testdata.json']

def setUp(self):
super(ViewsTest, self).setUp()
super(LoginViewsTest, self).setUp()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[SHOULD]

  • Django の TestCase の setUp で superは不要です。
 Wrapper around default __call__ method to perform common Django test
 set up. This means that user-defined Test Cases aren't required to
 include a call to super().setUp().

refs https://github.com/django/django/blob/master/django/test/testcases.py#L192

@@ -28,7 +30,7 @@ def test_login(self):
'password': 'password',
})
self.assertEquals(response.status_code, 302)
self.assert_(response['Location'].endswith(settings.LOGIN_REDIRECT_URL))
self.assertTrue(response['Location'].endswith(settings.LOGIN_REDIRECT_URL))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

@@ -58,41 +60,122 @@ def test_fail_login_blank_fields(self):
def test_bad_redirect_space(self):
bad_next_url = 'test space url'
self.assertContains(self.client.get('/account/login/'), '<form')
response = self.client.post('/account/login/?%s=%s' % (REDIRECT_FIELD_NAME, quote(bad_next_url)), {
response = self.client.post('/account/login/', {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

})
self.assertEquals(response.status_code, 302)
self.assert_(response['Location'].endswith(settings.LOGIN_REDIRECT_URL))
self.assertTrue(response['Location'].endswith(settings.LOGIN_REDIRECT_URL))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[MAY] LOGIN_REDIRECT_URL を明示的に設定した方が良い?

  • settings.LOGIN_REDIRECT_URL が具体的にどんな値なのかよくわからない(多分Djangoのデフォルト値?)

  • よくわからないからテストケースが妥当なのか判別つかない

  • settings.py に定義 or override_settingsを利用して、LOGIN_REDIRECT_URLがなんなのか明示的にした方が良さそう。

  • 例えば、 settings.LOGIN_REDIRECT_URL="/" だとしたら、bad_next_url とendswith で一致してバグっててもテストが通ってしまいそう。

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • settings.py に定義 & override_settingsを利用のテストケース追加

self.assertEquals(response.status_code, 302)
self.assertTrue(response['Location'].endswith('/some/url?param=http://example.com/'))

def test_ok_redirect(self):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

class LogoutViewsTest(DjangoTestCase):
fixtures = ['authutils_testdata.json']

@mock.patch('newauth.views.render')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[SHOULD] mockは無くてもテストできる

  • mockが必要な場面になったらmockを使う方が良いかと。
  • 普通に response 受け取ってテストすれば良い。
    def test_get_logout_default(self):
        """
        logout with HTTP GET
        default LOGOUT_render_URL settings
        """
        res = self.client.get('/account/logout/')
        self.assertEqual(res.status_code, 200)

redirect_url = 'http://example.com/path/to/resource/'
self.client.get('/account/logout/?%s=%s' % (REDIRECT_FIELD_NAME, quote(redirect_url)))
mock_render.assert_called()

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[MAY] 可能なら next_page 引数がある場合もテストする。

  • login, logout 両方の view で next_page引数が受け取れるようになってる。
  • 可能であれば それらを受け取る場合の分岐もテストケースにあると良さそう。(現状そのままでは self.cleint ではテストできないから面倒だけど)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[MAY] ついでに logout_then_login のテストがあっても良い。

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logout_then_login のテスト

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

next_page 引数がある場合もテスト

  • こちらすぐにどうやるかわからなかったので、Issueたてて、ひとまず保留したいと思います。

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

こちらのテストケースが該当すると思います。
https://github.com/beproud/django-newauth/blob/master/tests/test_api.py#L114

  • 私が言ってるのは、views.pyの下の方にひっそりと存在してる logout_then_login というview関数の話です。

https://github.com/beproud/django-newauth/blob/master/newauth/views.py#L85-L87

Copy link
Member

@tell-k tell-k Sep 6, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

こちらすぐにどうやるかわからなかったので、Issueたてて、ひとまず保留したいと思います。

  • 良いと思います。テストを書くのであれば以下の方法が考えれます。

案A. overriede_settings で ROOT_URL_CONF を上書きする。

案B. login, logout を 関数としてテストする。

  • self.clientは使わずにnext_pageが渡された場合の分岐をテストする。
  • request は RequestFactoryなどで作って パスする

Copy link
Contributor Author

@mtb-beta mtb-beta Sep 6, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Issue #14

Copy link
Member

@tell-k tell-k left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@mtb-beta mtb-beta merged commit d605992 into master Sep 6, 2018
@mtb-beta mtb-beta deleted the safe_redirect branch September 6, 2018 05:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants