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

Running convert-token for the second time returns html headers with user environment variables #147

Closed
snowcrash-arin opened this issue Nov 17, 2022 · 12 comments
Assignees
Labels
bug Something isn't working documentation Improvements or additions to documentation drf-oauth2 social-auth-pipeline

Comments

@snowcrash-arin
Copy link

Describe the bug
Running convert-token for the second time using the same backend token exposes user environment variables.

To Reproduce
Steps to reproduce the behavior:

  1. Get backend token(I used Google)
  2. Run curl with -i parameter(-i shows header contents)
  3. Run it again to expose the environment variables.

Expected behavior
Don't expose user environment variables, doesn't matter how many times convert-token gets run with a same token.

Screenshots
Running the convert-token for the first time:
Screenshot 2022-11-17 at 10 29 27 AM

Running the convert-token for the second time with the same backend token:
Screenshot 2022-11-17 at 10 46 27 AM

Desktop (please complete the following information):

  • OS: Ventura 13.0.1
  • Browser: Chrome
  • Version: 107.0.5304.110(arm64)
  • curl 7.84.0 (x86_64-apple-darwin22.0)

PIP Freeze
asgiref==3.5.2
cachetools==5.2.0
certifi==2022.9.24
cffi==1.15.1
charset-normalizer==2.1.1
cryptography==38.0.1
defusedxml==0.7.1
Deprecated==1.2.13
Django==4.1.2
django-environ==0.9.0
django-filter==22.1
django-oauth-toolkit==2.2.0
djangorestframework==3.14.0
drf-social-oauth2==1.2.1
ecdsa==0.18.0
google-api-core==2.10.1
google-auth==2.12.0
google-auth-httplib2==0.1.0
google-cloud-secret-manager==2.12.5
googleapis-common-protos==1.56.4
grpc-google-iam-v1==0.12.4
grpcio==1.49.1
grpcio-status==1.49.1
httplib2==0.20.4
idna==3.4
jwcrypto==1.4.2
Markdown==3.4.1
oauthlib==3.2.2
proto-plus==1.22.1
protobuf==4.21.7
psycopg2==2.9.4
pyasn1==0.4.8
pyasn1-modules==0.2.8
pycparser==2.21
PyJWT==2.6.0
pyparsing==3.0.9
python-jose==3.3.0
python3-openid==3.2.0
pytz==2022.5
requests==2.28.1
requests-oauthlib==1.3.1
rsa==4.9
six==1.16.0
social-auth-app-django==5.0.0
social-auth-core==4.3.0
sqlparse==0.4.3
uritemplate==4.1.1
urllib3==1.26.12
wrapt==1.14.1

Django settings.py
"""
Django settings for Timesheet project.

Generated by 'django-admin startproject' using Django 4.1.2.

For more information on this file, see
https://docs.djangoproject.com/en/4.1/topics/settings/

For the full list of settings and their values, see
https://docs.djangoproject.com/en/4.1/ref/settings/
"""

from pathlib import Path
import io
import os
from urllib.parse import urlparse

import environ
from google.cloud import secretmanager

Build paths inside the project like this: BASE_DIR / 'subdir'.

BASE_DIR = Path(file).resolve().parent.parent

[START gaestd_py_django_secret_config]

env = environ.Env(DEBUG=(bool, False))
env_file = BASE_DIR / "../.env"

if os.path.isfile(env_file):
# Use a local secret file, if provided

env.read_env(env_file)

[START_EXCLUDE]

elif os.getenv("TRAMPOLINE_CI", None):
# Create local settings if running with CI, for unit testing

placeholder = (
    f"SECRET_KEY=a\n"
    f"DATABASE_URL=sqlite://{BASE_DIR / 'db.sqlite3'}"
)
env.read_env(io.StringIO(placeholder))

[END_EXCLUDE]

elif os.environ.get("GOOGLE_CLOUD_PROJECT", None):
# Pull secrets from Secret Manager
project_id = os.environ.get("GOOGLE_CLOUD_PROJECT")

client = secretmanager.SecretManagerServiceClient()
settings_name = os.environ.get("SETTINGS_NAME", "django_settings")
name = f"projects/{project_id}/secrets/{settings_name}/versions/latest"
payload = client.access_secret_version(name=name).payload.data.decode("UTF-8")

env.read_env(io.StringIO(payload))

else:
raise Exception("No local .env or GOOGLE_CLOUD_PROJECT detected. No secrets found.")

SECRET_KEY = env("SECRET_KEY")

SECURITY WARNING: don't run with debug turned on in production!

Change this to "False" when you are ready for production

DEBUG = env("DEBUG")

[START gaestd_py_django_csrf]

SECURITY WARNING: It's recommended that you use this when

running in production. The URL will be known once you first deploy

to App Engine. This code takes the URL and converts it to both these settings formats.

APPENGINE_URL = env("APPENGINE_URL", default=None)
if APPENGINE_URL:
# Ensure a scheme is present in the URL before it's processed.
if not urlparse(APPENGINE_URL).scheme:
APPENGINE_URL = f"https://{APPENGINE_URL}"

ALLOWED_HOSTS = [urlparse(APPENGINE_URL).netloc]
CSRF_TRUSTED_ORIGINS = [APPENGINE_URL]
SECURE_SSL_REDIRECT = True

else:
ALLOWED_HOSTS = ["*"]

[END gaestd_py_django_csrf]

Application definition

INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'rest_framework',
'oauth2_provider',
'social_django',
'drf_social_oauth2'
]

MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

ROOT_URLCONF = 'Timesheet.urls'

TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [BASE_DIR, 'templates'],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
'social_django.context_processors.backends',
'social_django.context_processors.login_redirect',
],
},
},
]

WSGI_APPLICATION = 'Timesheet.wsgi.application'

REST_FRAMEWORK = {
# Use Django's standard django.contrib.auth permissions,
# or allow read-only access for unauthenticated users.
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.DjangoModelPermissionsOrAnonReadOnly'
],
'DEFAULT_AUTHENTICATION_CLASSES': (
'oauth2_provider.contrib.rest_framework.OAuth2Authentication', # django-oauth-toolkit >= 1.0.0
'drf_social_oauth2.authentication.SocialAuthentication',
),
}

AUTHENTICATION_BACKENDS = (
'social_core.backends.google.GoogleOAuth2',
'drf_social_oauth2.backends.DjangoOAuth2',
'django.contrib.auth.backends.ModelBackend',
)

Google configuration

SOCIAL_AUTH_GOOGLE_OAUTH2_KEY = 'GOOGLE_KEY'
SOCIAL_AUTH_GOOGLE_OAUTH2_SECRET = 'GOOGLE_SECRET'

Define SOCIAL_AUTH_GOOGLE_OAUTH2_SCOPE to get extra permissions from Google.

SOCIAL_AUTH_GOOGLE_OAUTH2_SCOPE = [
'https://www.googleapis.com/auth/userinfo.email',
'https://www.googleapis.com/auth/userinfo.profile',
]

Database

https://docs.djangoproject.com/en/4.1/ref/settings/#databases

Database

[START db_setup]

[START gaestd_py_django_database_config]

Use django-environ to parse the connection string

DATABASES = {"default": env.db()}

If the flag as been set, configure to use proxy

if os.getenv("USE_CLOUD_SQL_AUTH_PROXY", None):
DATABASES["default"]["HOST"] = "127.0.0.1"
DATABASES["default"]["PORT"] = 5432

Password validation

https://docs.djangoproject.com/en/4.1/ref/settings/#auth-password-validators

AUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
},
]

Internationalization

https://docs.djangoproject.com/en/4.1/topics/i18n/

LANGUAGE_CODE = 'en-us'

TIME_ZONE = 'UTC'

USE_I18N = True

USE_TZ = True

Static files (CSS, JavaScript, Images)

https://docs.djangoproject.com/en/4.1/howto/static-files/

STATIC_ROOT = "static"
STATIC_URL = "/static/"
STATICFILES_DIRS = []

Default primary key field type

https://docs.djangoproject.com/en/4.1/ref/settings/#default-auto-field

DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'

@snowcrash-arin
Copy link
Author

To add more details, running convert_token a second time with a different backend token doesn't yield the same result. The issue only happens when running for the second time with the same backend token.

@fahedmahidi
Copy link

To add even more details : the second request fails.

@wagnerdelima
Copy link
Owner

Guys, you are clearly not reading the docs. This package has almost 12k downloads a month. Surely I have misconfigured the settings.

@snowcrash-arin
Copy link
Author

@wagnerdelima Can you provide details on which settings were misconfigured and confirm if you were able to replicate the issue? Just fyi, this behavior happens not only from curl, but also on the DRF API page.

@wagnerdelima
Copy link
Owner

@snowcrash-arin that's my concern exactly. I was not able to replicate the issue.

@wagnerdelima wagnerdelima added documentation Improvements or additions to documentation drf-oauth2 labels Apr 17, 2023
@wagnerdelima wagnerdelima self-assigned this Apr 17, 2023
@wagnerdelima
Copy link
Owner

wagnerdelima commented Apr 21, 2023

@fahedmahidi @snowcrash-arin FYI:
I completely misread this issue. In your instructions, you added "Run curl with -i parameter(-i shows header contents)". Yes, it will add the headers in the response because you are passing -i:

image

@snowcrash-arin
Copy link
Author

@fahedmahidi @snowcrash-arin FYI: I completely misread this issue. In your instructions, you added "Run curl with -i parameter(-i shows header contents)". Yes, it will add the headers in the response because you are passing -i:

image

Hi @wagnerdelima

If you look at the screenshot, it shows user environments, not just the headers, which is security concern. Additionally, I made note of the fact that this issue is occurring on the DRF API page.

@wagnerdelima
Copy link
Owner

@snowcrash-arin what do you mean by "DRF API page"?

@wagnerdelima
Copy link
Owner

I am investigating this issue @snowcrash-arin. So far I have got no clue on how to solve it. Any ideas will be welcome.

@wagnerdelima
Copy link
Owner

wagnerdelima commented Apr 26, 2023

@snowcrash-arin @fahedmahidi I believe this is fixed now. Please retest it with the latest published version 2.1.1.

Also, check the newly published readthedocs page: https://drf-social-oauth2.readthedocs.io/en/latest/

@snowcrash-arin
Copy link
Author

Thanks @wagnerdelima! I'll test it and let you know sometime this month. I stopped working on it due to the security concern.

@wagnerdelima
Copy link
Owner

@snowcrash-arin please do! Let me know :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working documentation Improvements or additions to documentation drf-oauth2 social-auth-pipeline
Projects
Development

No branches or pull requests

3 participants