How to force Google App Engine [python] to use SSL (https)? - python

I write a web app on Google App Engine using Python.
Users can access my site at http://[youraccount].appspot.com and https://[youraccount].appspot.com
How do I redirect the http traffic to the https site.
In other words, how do I force this site to use SSL(https) for security purpose (and for better SEO)?

Just add a secure parameter to the app.yaml file.
handlers:
- url: /youraccount/.*
script: accounts.py
login: required
secure: always
See Configuring Secure URLs in app.yaml
Google App Engine supports secure connections via HTTPS for URLs using
the *.appspot.com domain. When a request accesses a URL using HTTPS,
and that URL is configured to use HTTPS in the app.yaml file, both the
request data and the response data are encrypted by the sender before
they are transmitted, and decrypted by the recipient after they are
received. Secure connections are useful for protecting customer data,
such as contact information, passwords, and private messages.

For a Django project running on Google App Engine in the Flexible Environment, setting secure: always in app.yaml doesn't work [Google Cloud docs].
Instead, in my settings.py file, I added the following [Django docs]:
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
SECURE_SSL_REDIRECT = True
SECURE_HSTS_SECONDS = 31536000
SECURE_HSTS_INCLUDE_SUBDOMAINS = True
Note that SECURE_PROXY_SSL_HEADER is needed because the servers sit behind a load balancer.

Related

Flaskdance doesn't generate a HTTPS uri

I'm trying to set up Google sign-in using Flask dance for a flask based website:
from flask_dance.contrib.google import make_google_blueprint, google
blueprint = make_google_blueprint(
client_id= "CLIENT_ID",
client_secret="CLIENT_SECRET",
scope=[
"https://www.googleapis.com/auth/plus.me",
"https://www.googleapis.com/auth/userinfo.email",
]
)
app.register_blueprint(blueprint, url_prefix="/google_login")
And as the documentation suggests, I have the view set up like this:
#app.route('/google_login')
def google_login():
if not google.authorized:
return redirect(url_for("google.login"))
resp = google.get("/oauth2/v2/userinfo")
assert resp.ok, resp.text
return "You are {email} on Google".format(email=resp.json()["email"])
When I was testing I set the environment variable, OAUTHLIB_INSECURE_TRANSPORT to 1 by using
export OAUTHLIB_INSECURE_TRANSPORT=1
And now even after I've removed the environment variable, for some reason the Flaskdance seems to always resolve the URI to a http instead of HTTPS.
This is evident from the redirect uri mismatch error I'm getting (here website refers to the domain name):
The redirect URI in the request,
http://"website"/google_login/google/authorized, does not match
the ones authorized for the OAuth client.
And here are the authorized redirect URIs I've set up in my Google cloud console:
https://"website"/google_login/google/authorized
https://www."website"/google_login/google/authorized
I tried unsetting the environment variable using this command:
unset OAUTHLIB_INSECURE_TRANSPORT
What am I missing here? Any help would be appreciated.
If Flask-Dance is generating http URLs instead of https, that indicates that Flask (not Flask-Dance, but Flask itself) is confused about whether the incoming request is an https request or not. Flask-Dance has some documentation about how to resolve this problem, and the most likely cause is a proxy server that handles the HTTPS separately from your application server.
The fix is to use a middleware like werkzeug's ProxyFix to teach Flask that it's behind a proxy server. Here's how you can use it:
from werkzeug.middleware.proxy_fix import ProxyFix
app.wsgi_app = ProxyFix(app.wsgi_app, x_for=1, x_proto=1)
I had the same problem and in my case adding this to my Apache VirtualHost config solved it:
RequestHeader set X-Forwarded-Proto "https"
My Flask is running behind an Apache proxy but Nginx would also have similar issues, potentially.

Google App Engine flask SSL and OAuth2 problems

i'm trying to create flask web application on google app engine python 3 flexible env with oauth2 authentication on custom domain.
So, the problems are following :
1)I have added custom domain to my project, and also added SSL to that custom domain. In google cloud console everything seems fine, but SSL not showing/working on my custom domain.
Maybe problem is in my dispatch file?
dispatch:
- url: 'mycustomdomain.com/'
service: default
- url: 'www.mycustomdomain.com/'
service: default
2)I can't login in despite having SSL on https://[project-id].appspot.com. After pressing "Login with Google" i'm redirecting to /authorize, where i choose account from which i want to login. After that happens redirect to /oauth2callback, https mystically changes to http and i can't login, getting following error InsecureTransportError: (insecure_transport) OAuth 2 MUST utilize https.
Python authorize :
#app.route('/authorize')
def authorize():
flow = google_auth_oauthlib.flow.Flow.from_client_secrets_file(
CLIENT_SECRETS_FILE, scopes=SCOPES)
flow.redirect_uri = flask.url_for('oauth2callback', _external=True)
authorization_url, state = flow.authorization_url(
access_type='offline',
include_granted_scopes='true')
flask.session['state'] = state
return flask.redirect(authorization_url)
Python oauth2callback:
#app.route('/oauth2callback')
def oauth2callback():
state = flask.session['state']
flow = google_auth_oauthlib.flow.Flow.from_client_secrets_file(
CLIENT_SECRETS_FILE, scopes=SCOPES, state=state)
flow.redirect_uri = flask.url_for('oauth2callback', _external=True)
authorization_response = flask.request.url
flow.fetch_token(authorization_response=authorization_response)
credentials = flow.credentials
flask.session['credentials'] = credentials_to_dict(credentials)
session = flow.authorized_session()
flask.session['username_output'] = session.get(
'https://www.googleapis.com/userinfo/v2/me').json()
return flask.redirect(flask.url_for('map'))
When testing locally i'm using os.environ['OAUTHLIB_INSECURE_TRANSPORT'] = '1' and locally it is working perfectly fineAny suggestions??
SOLUTION
Problem solved -
1)i forgot to add SSL certificate to domain(where domain is hosted), thats why SSL wouldn't show.. I know, so lame mistake.
2)First, I forced SSL with SSLify for flask, but that didn't solve the problem, which was in following line
authorization_response = flask.request.url
no matter what, this line of code gave me http://, i tried to change every scheme that i could find to https in flask.url_for(_scheme='https'), but that didn't help either, so for now, my workaround is
authorization_response = authorization_response.replace('http', 'https')
i know, not the best solution, but it works.
I ran into this issue as well and found out that the issue stems from authorization_response = flask.request.url because that URL defaults to HTTP rather than HTTPS.
My hacky solution was to run a quick regex substitution and force the URL to be HTTPS:
import re
....
authorization_response = request.url
new_auth = re.sub(
"http:",
"https:",
authorization_response
)
flow.fetch_new_token(new_auth)
There are certainly better ways to accomplish this, but it works.
I ran into this error because I was using nginx in front of gunicorn/flask.
I fixed it by adding
proxy_set_header X-Forwarded-Proto $scheme;
to my nginx config, and then adding
from werkzeug.middleware.proxy_fix import ProxyFix
application.wsgi_app = ProxyFix(application.wsgi_app, x_proto=1)
to the filing being called by gunicorn.
For your specific problem on the GAE Python 3 Flex environment, you'll need to configure gunicorn to do the same thing.
App Engine terminates the HTTPS connection at the load balancer and forwards the request to your application. Most applications do not need to know if the request was sent over HTTPS or not, but applications that do need this information should configure Gunicorn to trust the App Engine proxy in their gunicorn.conf.py:
forwarded_allow_ips = '*'
secure_scheme_headers = {'X-Forwarded-Proto': 'https'}
Gunicorn will now ensure that the wsgi.url_scheme to 'https', which most web frameworks will use as indication of the request is secure. If your WSGI server or framework doesn't support this, just check the value of the X-Forwarded-Proto header manually.
https://cloud.google.com/appengine/docs/flexible/python/runtime#recommended_gunicorn_configuration

Set "secure" attribute for Flask cookies

I am running a Flask app using uWSGI and Nginx. I want make it compliant with PCI DSS. Running the scan gives the error Cookie Does Not Contain The "secure" Attribute. How do I set the secure attribute for cookies in Flask?
I have added the following line in my Nginx file but it didn't work.
proxy_cookie_path / "/; secure;";
The secure flag for Flask's session cookie can be enabled in the Flask configuration.
SESSION_COOKIE_SECURE = True
To set it for other cookies, pass the secure flag to response.set_cookie.
response = app.make_response('<p>Hello, World!</p>')
response.set_cookie('name', 'World', secure=True)

Restrict view to only be accessible by App Engine internal network

I would like to find a way to restrict a view (request handler) to only be called from within the Google App Engine internal network from within my view and not within app.yaml.
For example, I have a view to handle inbound email within my Flask application
#app.route('/_ah/mail/notifications#example.appspotmail.com', methods=['POST'])
def inbound_notification_email():
from google.appengine.api import mail
message = mail.InboundEmailMessage(request.data)
...
return '' # 200 OK
While I know I could put all my mail handlers in their own file / wsgi instance like so:
handlers:
- url: /_ah/mail/.+
script: inbound_mail.app
login: admin
I would prefer not to have to do this as I'm using Flask instead of Webapp. Right now the request works as setup above, but it is exposed to the world.
Inspecting the request to my inbound_notification_email() view, I see X-App-Country in the request header is set to ZZ and the request's remote address is 0.1.0.20. I know the 0.x.x.x IP range is IANA reserved for local networks so it seems logical that checking if request.remote_address starts with "0." would work, but I'm not sure if all internal requests within App Engine are always handled this way (push queues and xmpp come to mind).
One thing I was surprised to see was users.is_current_user_admin() returns False within inbound_notification_mail() even though you're to set login: admin when using Webapp.

How to make all the Handlers(pages) on a google app engine website https

Hi I want to make my all the handlers loaded as https in google app engine project. Can any one know a way to do it? is there a configuration which we could apply for all the handlers?
Do you use webapp2? With webapp2 you can specify the URI schemes allowed for a route:
webapp2.Route(r'/products', handler='handlers.ProductsHandler', name='products-list', schemes=['https'])
See: http://webapp-improved.appspot.com/guide/routing.html
In your app.yaml, use secure: always for each of the handlers where you'd like to require https. For example:
handlers:
- url: /youraccount/.*
script: accounts.py
login: required
secure: always
You can read more about the secure URLs in the documentation.

Categories

Resources