How safe is my Flask REST API? - python

My website is on http protocol. My flask API is secured via flask-httpauth (https://github.com/miguelgrinberg/Flask-HTTPAuth).
There is a Tornado web server in front of my Flask API which listens on a private port 5000. Client API requests first go to Tornado server which then calls the Flask API
This is the flow I've got going:
My website (on http) ---> corpauthentication (on https) --> back to my website (http) --> client calls Tornado server --> Tornado calls Flask API and returns results
How safe is my API and website? I was reading this link Security of python flask REST API using HTTP Basic Authentication and it seems to me that the API is secure but I can never be sure.
If its not safe, what else do you think I can do to make it more secure? Since corpauthentication is required to get in, I feel on the UI side it is pretty safe. But lets say someone is listening on my port 80, will they be able to track any API requests made even when there is tornado + httpbasic auth in place?
This is my Tornado Server code:
from tornado.wsgi import WSGIContainer
from tornado.ioloop import IOLoop
from tornado.web import FallbackHandler, RequestHandler, Application
from flaskfile import app
class MainHandler(RequestHandler):
def get(self):
self.write("This message comes from Tornado ^_^")
tr = WSGIContainer(app)
application = Application([
(r"/tornado", MainHandler),
(r".*", FallbackHandler, dict(fallback=tr)),
])
if __name__ == "__main__":
application.listen(5000)
IOLoop.instance().start()
This is how I'm calling the API from my Javascript:
$.ajax({
url: 'http://x.x.x:5000/data',
type: 'GET',
dataType: 'json',
async: false,
headers: {
"Authorization": "Basic " + btoa("username" + ":" + "password")
},
data: {start: startdate, end: enddate},
success: function(result) {
data = result.results;
}
});

No, this is not secure - the comments in the other question you linked to are entirely correct. (BTW your question is really a duplicate of that).
Authentication over regular unencrypted HTTP is never secure - the username and password will be visible to any device between the user and the webserver, in plain text. As a first step you should implement SSL/TLS to encrypt the authentication information.
Tornado really needs to sit behind a web proxy of some sort. You could use either Apache or Nginx to fulfil this role. There are instructions for setting up Tornado+Nginx in this related question.

Related

Flask-Session cookie works on other browsers for ip address & domain, but for chrome it only works on ip address

I found a question with this same problem, except it was 7 years old, and they had the opposite issue, where chrome worked for their domain, but not IP. I need this application to work on the domain, not the ip, which is unfortunate.
If I Have some basic code like this:
Flask:
app = Flask(__name__)
from dotenv import load_dotenv
load_dotenv()
SECRET_KEY = os.getenv('FLASK_APP_SECRET_KEY')
SESSION_TYPE = 'filesystem'
app.config.from_object(__name__)
Session(app)
CORS(app)
#app.route('/give', methods = ['GET'])
#cross_origin(supports_credentials=True)
def user_make(id):
session['Hi'] = 'There'
return 'ye'
#app.route('/take', methods = ['GET'])
#cross_origin(supports_credentials=True)
def user_load(id):
return session['Hi']
reactjs:
let data = new FormData()
return axios
.get('12.34.56.78' + '/give', data, {
headers: {
"Content-Type": "multipart/form-data",
},
}).then(
return axios
.take('12.34.56.78' + '/take', data, {
headers: {
"Content-Type": "multipart/form-data",
},
}))
On a server with ip='12.34.56.78' and domain 'example.com':
When using the domain or ip on safari, the output is
'there'
for both
however, on chrome,
for ip the output is
'there'
however, for domain, the output is
Key Error
edit:
Some more info:
This is on an AWS ec2 ubuntu server, which is running on port 80 for the frontend and 5000 for the backend. I connected the ip address to the domain name with AWS Route 53... just in case this is relevant. To access the frontend, one can go to the ip or the domain, whereas to access the backend, one must go to ip:5000
Any more info needed?
Is this fixable?
Thanks!
I think the problem is with how google chrome manage the cookies. It's the 'SameSite' attribute. Back on July 14th, 2020, Google started gradually rolling out a new browser policy with a few major changes. One that treats cookies as SameSite=Lax by default, if no SameSite attribute is specified. The other deprecates and removes the use of cookies with the SameSite=None attribute that did not include the Secure attribute. That means that any cookie that requests SameSite=None but is not marked Secure is now being rejected. This means that the front-end can’t contact the back-end and the site is not working. To fix it, you just need to make sure that when your _SESSION_ID cookie is created it includes the SameSite=None and Secure attributes.
P.S.1: Based on the article of Caleb. Back-end is Ruby on Rails but i don't think this is an issue.
P.S.2: Before change anything, try other chrome-based browsers like Vivaldi, Comodo or even the new Microsoft Edge.

How to contact flask-restful api from react native app?

i am new to backend dev. I try to developp a flask restful api. I followed the documentation and made the suggested minimal api, that is just returning a jsonified dict. With postman, curl and in the browser, no problem, the api is running and responds to my requests.
From my react native app however, i always get a Netwrok Error.
I tried lots of things:
- multiple IP addresses for the flask serveur
- differents ways to use axios
- different os : win10, ubuntu
- different endpoints : /, /api, /test
- different ways of writing the api (flask-restful class, flask app functions ...)
- manips from web documentations : dev mode of my device, chrome dev tools (port forwarding)
- i asked a react native developper to check my client-side code, nothing wrong according to him,
precisions :
- python code runs under a conda virtual env
- appli runs under node js and expo
- container folders of my app and my api are at the same level
- the flask server does not respond 404 or whatever, it does not respond at all, the error returned in react native is a network error
- depending on url, the network errors occurs immediately or after a 2 minutes delay
- flask shows requests and status when called by postman, curl, chrome, but does not react when i press my buttons from the react native app
Here is (one of) my python code:
from flask import (Flask, jsonify)
# from flask_restful import Resource, Api
from flask_cors import CORS, cross_origin
app = Flask(__name__)
CORS(app)
#app.route("/api", methods=["GET"])
def get():
return jsonify(hello='bouh !')
if __name__ == '__main__':
app.run(host="0.0.0.0", port=5000, debug=True)
and here is the clientside code:
import React, {Component} from 'react';
import { View, Button } from 'react-native';
import axios from 'axios';
import { sharedStyles } from '../../SHARED/_shared';
var url = "http://192.168.1.16:5000/api";
export default class PrevCommune extends Component {
constructor(props){
super(props);
this.navigation=this.props.navigation;
};
getAxios=()=>{
axios.get(`${url}`).then((response)=>{
console.log("succes axios :",response);
}).catch((error)=>{
console.log("fail axios :", error);
});
};
getFetch=()=>{
fetch(url).then((response)=>{
console.log("succes fetch :",response)
}).catch((error)=>{
console.log("fail fetch :",error)
})
}
render(){
return (
<View style={sharedStyles.mainContainer}>
<Button onPress={()=>this.getAxios()} title={"get axios"}></Button>
<Button onPress={()=>this.getFetch()} title={"get fetch"}></Button>
</View>
);
};
};
And the lines returned by requests:
fail axios : [Error: Network Error]
fail fetch : [TypeError: Network request failed]
I saw lots of tutos, videos, articles on flask api but i didn't find where i am wrong. Please tell me if you have any ideas ! I think both client and server codes are ok, the problem seems to be that my requests are blocked by something.
Solved : the probleme was my firewall ... thank you ricardo for the CORS doc =)
What worked for me was to change the IP address of localhost to my network IP in the api call in react native and then starting the flask application using the below command.
flask run --host=0.0.0.0

accessing client's x509 certificate from within twisted web WSGI app

I have set up a twisted + flask https server that also does certificate-based client authentication by following the documentation at the Twisted site here. So far, so good.
In addition to authenticating the client using a certificate, the application code within the flask app needs the user name (which is present in the client x509 certificate) in order to do its job. I couldn't find an easy way to access this information. The information (based on the documentation) seems to be in the pyopenssl X509Name object at the time it does authentication, and I need the identity at the flask layer every time I process a request from that client.
The request object flask is getting did not seem to have this information (unless I read it wrong), so I assume I need to modify some options at the Twisted level to send them through to flask. I also need to somehow get them out of the OpenSSL layer.
How would you do this?
Updated: using HTTPChannel.allHeadersReceived instead of Protocol.dataReceived for support of chunked requests.
You can use HTTP headers to store connection information: set them up in HTTPChannel.allHeadersReceived method and retrieve from flask.request.headers, e.g.:
from twisted.application import internet, service
from twisted.internet import reactor
from twisted.web.http import HTTPChannel
from twisted.web.server import Site
from twisted.web.wsgi import WSGIResource
from flask import Flask, request
app = Flask('app')
#app.route('/')
def index():
return 'User ID: %s' % request.headers['X-User-Id']
class MyHTTPChannel(HTTPChannel):
def allHeadersReceived(self):
user_id = 'my_user_id'
req = self.requests[-1]
req.requestHeaders.addRawHeader('X-User-Id', user_id)
HTTPChannel.allHeadersReceived(self)
class MySite(Site):
protocol = MyHTTPChannel
application = service.Application('myapplication')
service = service.IServiceCollection(application)
http_resource = WSGIResource(reactor, reactor.getThreadPool(), app)
http_site = MySite(http_resource)
internet.TCPServer(8008, http_site).setServiceParent(service)
I'm not familiar with using client certificates in twisted. I assume you can retrieve its information in Protocol.transport.

write a reverse proxy in node js to call the api in django

My frontend code is running in angular at node httpserver port 127.0.0.1:8081
My backend services runnning in python django framework at port 127.0.0.1:9000
While calling my backend servies from angular http methods throws cors exception
so i wrote a proxy controller in my node js
var http = require('http'),
httpProxy = require('http-proxy');
var proxy = httpProxy.createProxyServer();
http.createServer(function (req, res) {
// This simulates an operation that takes 500ms to execute
setTimeout(function () {
proxy.web(req, res, {
target: 'http://127.0.0.1:9000/dummy/'
});
}, 500);
}).listen(8080, "127.0.0.1");
to listen and bind at angular. i run as the node proxycontroller.js, results a another port no 127.0.0.1:8080
from proxy controller it calls my backend service and result json but from the angular js calling the proxy controller from the http.get() method results cors problem
please help to solve this problem.
Enable CORS in Django
Uses this third-party lib django-cors to do it.
You are getting CORS because you are making the call from your AngularJS app and its host is 127.0.0.1:8081, different from your Django app host 127.0.0.1:9000
The error said CORS is supported only for specific protocals like http:// etc. So you have to add http to that url. When you say, 127.0.0.1, the browser is unable to understand which protocol to use to access that url, should it be using http:// or data:// or chrome:// etc. So you've to explicitly say http://
You have to configure cors in the backend. The backend (the API server) much explcitly say a site is allowed using the http header I specified earlier. Otherwise, I can open your website, edit the frontend using chrome console and remove all the security stuff. It has the be in the backend.
as http://127.0.0.1:9000 from the front end angular app, dosent need to create proxy server to transfer the calls to backend service.

Using app engine oauth service

I'm trying to authenticate a python command line script agains my app engine application using oauth.
I'm following these instructions but I still don't get the "big picture" of how it works.
Is this api "ready to use"? or should I implement the request handlers for the oauth process?.
So far, I have my app deployed and I'm using this oauth library, I'm trying this example with the following values:
SERVER = 'myapp.appspot.com'
PORT = 443 # Also tried 80
REQUEST_TOKEN_URL = '/_ah/OAuthGetRequestToken'
ACCESS_TOKEN_URL = '/_ah/OAuthGetAccessToken'
AUTHORIZATION_URL = '/_ah/OAuthAuthorizeToken'
CALLBACK_URL = 'oob'
RESOURCE_URL = 'http://myapp.appspot.com/'
CONSUMER_KEY = 'myapp.appspot.com'
CONSUMER_SECRET = 'AaB8BtzxM7Dr7wz9Dxc5y6gG'
Do I have to implement any request handler on the server side? Do I have to enable this api somewhere?
Thanks for any clarification.
EDIT: Here is the output of the client script running:
$ python test.py ** OAuth Python Library Example **
* Obtain a request token ...
REQUEST (via headers)
parameters: {'oauth_nonce': '64747931', 'oauth_timestamp': 1325595310, 'oauth_consumer_key': 'myapp.appspot.com', 'oauth_signature_method': 'HMAC-SHA1', 'oauth_version': '1.0', 'oauth_signature': 'rBMJdn8+n0yXei38tDMfHjYKxyM=', 'oauth_callback': 'oob'}
And the response I'm getting is:
<html><head>
<meta http-equiv="content-type" content="text/html;charset=utf-8">
<title>400 Bad Request</title>
</head>
<body text=#000000 bgcolor=#ffffff>
<h1>Error: Bad Request</h1>
<h2>Your client has issued a malformed or illegal request.</h2>
<h2></h2>
</body></html>
You don't need to implement handlers, the framework will do that for you.
All you need to do is to use the oauth.get_current_user() and everything will be handled for you from the server perspective.
Also you don't suppose to put the secret in the client (I don't know even if GAE give you any access to the secret).

Categories

Resources