I am a web-dev noob but I'll try my best to be clear about the problem, explain my approach, and what I've tried. I also include imports in-case those are causing the problem but I'm pretty sure I've isolated it to what I describe.
I am trying to use Flask-session to keep information private but some values are "lost". This code is trimmed down significantly for simplicity. The user lands on /send and we render the loading template. loading.js does fetch() on /deploy while it runs an animation, then() we go to /results when the deploy function is done.
loading.js
function navigate() {
window.location.href = 'results'; // redirect to results page when done!
}
// deploy the contract while the loading screen goes then navigate to results page
const data = fetch('deploy').then(navigate);
loopThroughMessages(messages);
main.py
from flask_session import Session
app = Flask(__name__,
static_folder='static',
template_folder='templates')
# for the session, i.e passing values
app.config["SESSION_PERMANENT"] = False
app.config["SESSION_TYPE"] = "filesystem"
app.config.from_object(__name__)
Session(app)
#app.route('/send')
def main():
# take url parameters and do stuff with them
return render_template('loading.html')
#app.route("/deploy")
def deploy_contract():
session['contract_address'] = some_fnc()
# fetch() requires that this function return a json
return {}
#app.route("/results")
def serve_results_page():
# pull saved values from the session
data = {'contract_key' : session['contract_address']
} # calling session here causes the error, the contract_address key doesn't exist
return render_template('results.html', data=data)
So contract_address is saved to the session but when we get to /results, the server has no way to associate that session with the client.
We want to keep our contract_address private so sending it to loading.js is not an option. I'm guessing that since http is stateless, I need to pass a cookie to and from my js and python files but I'm a bit lost on how to implement it. Are cookies unnecessary (because the server doesn't actually need to receive any data from my js files)? Should I be using redirects or something besides fetch()?
Hacky fixes, different approaches, and resources are all welcome. I feel like I'm close, like there's a simple way to use cookies that I'm overlooking.
I will be continuing to research and detail the approaches I'm considering
Edit1: Looking at Flask's should_set_cookie method
Try fetch with credentials:'include' to cause browsers to send a request with credentials included on the server side calls:
fetch('deploy', {
method: 'GET',
credentials: 'include'
}).then(navigate);
Using this, you will access session['contract_address'] in the results route.
The flask-session sets a cookie with a key session in your browser, fetch with credentials:'include' includes this cookie value in the network call.
Session in flask is implemented as a client session, saving all session content as client cookies. The flask-session extension provides some other server storage for session. E.g. app.config["SESSION_TYPE"] = "filesystem" save session content in a file on the server.
But both of the approaches still depends on Cookie. The server-side session storage need to get a session_id from client Cookie.
You need to enable cookie sending on Fetch API.
fetch, sending cookies
Related
These days I made a standard home page, a login page, and a user information page. In my login page using method POST and Ajax(Fetch), I send the e-mail and password to the server, the server responses the correct json with the user information that belongs to that e-mail. BUT I don't have an idea how that user information in JSON can use in another HTML document like a user information page.
I have tried so many things but nothing helped me.
form.addEventListener('submit',(e)=>{
e.preventDefault();
const data={}
data1.correo=email.value;
data1.contrasenia=password.value;
const miinit={
method:'POST',
headers: {"Content-type":"application/json"},
body:JSON.stringify(data1)}
const url='https://localhost:44351/api/login';
const myrequest =new Request(url,miinit)
const requestpost=()=>{
fetch(myrequest).then(response=>{console.log(response);return response.json()}).then(response=>{console.log(response)})
}
requestpost()
})
I'm assuming you are doing this all without any library or anything. With that being said, I'm assuming that each page transition is going to be a full page refresh. This means that your object returned by the API is only as good as the lifetime of your Login Page.
You have two options that will work with your setup:
Use localStorage to store the current users JSON payload.
const payload = { userId: 1, name: 'Test' };
// The item being saved to localStorage must be
// a string. You serialize/deserialize as your
// set/get if need be.
localStorage.setItem('currentUser', JSON.stringify(payload));
// On some other page, calling localStorage.getItem
// will give you the object
console.log(localStorage.getItem('currentUser'));
Assuming you have control of your backend code and there is a session available, create a new API Method that will return the current user object. You can call the getCurrentUser method on every page and that will return that logged in user.
Both of these options have security points to think about, but that isn't what this question is about. I think for you, the easiest option is to just save the object to Local Storage, but I would not recommend that for a production setup. If you are trying to do something that is production ready, I would recommend that you look into JWT Authentication.
https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage
https://jwt.io/introduction/
If you want to use the information coming back from the server in more than one page, you'd have to store it in LocalStorage or another persistent API. Data is not shared across pages.
I am working on a php website. I have a page called pageX.php, and on that page I make an Ajax call:
xmlhttp.open("GET", "/getData/dat?q=" + str, true);
I want to avoid displaying data that is contained in this request. Also I want to be able to respond to this request only if it comes from pageX.php, and nowhere else.
I have tried using PHP sessions but it didn't work – the server still responds to the request if you go directly from the browser address bar to
mysite.com/getData/dat?q=1
Can you help with a solution for this problem?
You said you tried using sessions. I'd say that would be a quick and easy way to do it without going down the path of various Authentication protocols like OAuth.
Using sessions would mean that if you use the same browser as your current pageX.php, you will be able to access mysite.com/getData/dat?q=1 from another tab.
This is because that browser would still be sending session cookies to your host. However, if you try using a new browser or a new device, you should not be able to make that request as you no longer have the same session cookies.
So your only risk is that someone else with access to your user's computer might be able to access PageX.php and their session.
So this is a good compromise, if you think that whoever is using PageX.php is not using it on a shared computer.
CORS is not required to send requests to resources on the same domain since it is sent from the same origin. The issue here is that you have an open route which takes in arguments that respond with sensitive information based on that primary key. You need to close that route.
In order to do so, you can use something like JWT to generate bearers for users. Sort of like, "Remember me tokens". This can be generated when the user authenticates and be stored as a session like so:
session_start();
/**
* Your authentication logic here
* Which for example returns a uid of 1
*/
$_SESSION['ssid'] = \Firebase\JWT\JWT::encode((object) ['uid' => 1], 'your_key');
Your closed route would now look something like this:
session_start();
header('Content-Type: application/json');
if(!isset(($bearer =& $_SESSION['ssid'])))
die(json_encode(['error' => 403])); // Forbidden
try {
$user = \Firebase\JWT\JWT::decode($bearer, 'your_key', array('HS256'));
// Your logic
// $user->uid to get the ID
} catch (Exception $e) {
// Handle exceptions where the bearer has either expired or is invalid
}
Then, (JQuery example) you'd be able to simply request the closed route like so:
(function ($) => {
$.get('/getData', (response) => {
// Logic
});
})(JQuery);
This now stops users being able to forge the request with their own arguments.
I am having a tiny issue wrapping my head around how I would go about deauthenticating a user in the most efficient manner in my React/Redux application. (Using Redux Thunk as well).
Getting the token from the server is a piece of cake. Currently I:
- Send POST request with email and pw
- Receive Token back from server
- Save it to local storage.
Any additional requests after that to the server requires attaching the token inside an AUTHORIZATION header, which is fine.
Lets say that I walk away from the app after being logged in for awhile, then come back and decide to try to view another protected route. Lets call this route, "/latest-videos", which sends out an AJAX request to fetch the latest videos.
When that request is sent, the server determines that my token has expired, and will not allow me access to this resource. Obviously, this is where I would want to implement some type of action creator in Redux that will go ahead and redirect the user to the login page.
Do I need to perform some type of check on every AJAX request to see if the token has expired? It seems like doing that would require unnecessary boilerplate in every file that I have an AJAX request written.
This is where things get muddy for me. If we assume the token has a predetermined expiration date, how can I go about deauthenticating a user in the most efficient manner, without filling my app with poor code design choices?
You can assume I have a folder in my app called "actions" that contains files like so:
- actions
- videos.js
- users.js
- projects.js
- ...
Within a given file inside of the actions folder, you could have multiple Thunks that perform AJAX calls. Here is boilerplate example of what one of them may look like:
export function fetchVideos(query) {
return async (dispatch) => {
try {
dispatch({ type: FETCH_VIDEOS })
let res = await axios.post(`${API_URL}/videos/search`, {
query: query
})
// dispatch some success action creator with the data
}
catch(e) {
console.error('ERROR FETCHING VIDEOS: ' + e)
dispatch({ type: FETCH_VIDEOS_FAILED })
}
}
}
Hopefully I am making some sense...any help would be appreciated.
No need to check the token in your react app. Your server should be the one checking your token’s validity/expiry upon every request.
Since we’re talking about client application consuming restful api calls, server should be the one restricting the resources upon invalid token and return the 401 unauthorized response.
Upon that response, client app should deauthenticate a user and redirect to the login page (or send a request with a refresh token if you have any to avoid unnecessary login action).
I have an app that has a REST api. I want it so that the only requests that can be made to the REST api are ones originating from the app itself. How can I do that?
I am using a node.js+express server too.
EDIT: the app is fully a public web app.
Simply define the header in your request, what this does is, it allows requests only from a certain domain, and instantly rejects any other domain.
response.set('Access-Control-Allow-Origin', 'domain.tld');
EDIT: IF you're really keen against web scraping stuff, you could make a function to double check client's origin.
function checkOrigin (origin) {
if (origin === "your.domain.tld") {
return true;
} else {
return false;
}
}
/* Handling it in response */
if (checkOrigin(response.headers.origin)) {
// Let client get the thing from API
} else {
response.write("Send them error that they're not allowed to use the API");
response.end();
}
Above example should work for the default HTTP/HTTPS module, and should also work for Express, if I'm not mistaken.
EDIT 2: To back my claim up that it should also work for Express, I found this quotation at their documentation;
The req (request) and res (response) are the exact same objects that Node provides, so you can invoke req.pipe(), req.on('data', callback), and anything else you would do without Express involved.
I would recommend using an API key from the client. CORS filters are too easy to circumvent.
A simple approach for securing a How to implement a secure REST API with node.js
Overview from above post:
Because users can CREATE resources (aka POST/PUT actions) you need to secure your api. You can use oauth or you can build your own solution but keep in mind that all the solutions can be broken if the password it's really easy to discover. The basic idea is to authenticate users using the username, password and a token, aka the apitoken. This apitoken can be generated using node-uuid and the password can be hashed using pbkdf2
Then, you need to save the session somewhere. If you save it in memory in a plain object, if you kill the server and reboot it again the session will be destroyed. Also, this is not scalable. If you use haproxy to load balance between machines or if you simply use workers, this session state will be stored in a single process so if the same user is redirected to another process/machine it will need to authenticate again. Therefore you need to store the session in a common place. This is typically done using redis.
When the user is authenticated (username+password+apitoken) generate another token for the session, aka accesstoken. Again, with node-uuid. Send to the user the accesstoken and the userid. The userid (key) and the accesstoken (value) are stored in redis with and expire time, e.g. 1h.
Now, every time the user does any operation using the rest api it will need to send the userid and the accesstoken.
I'm developing an hybrid mobile app using HTML/CSS/JS, I'm going over security with login information, the system I have set up creates an hash after a user logs in, this hash has a time limit and is set via localStorage
Essentially, I would have something like this is localstorage:
hash
5f4a09cfec2a6d8f306eecb3844e33e9
hash_expiration
1373012945
password
*encryted user password*
This hash is sent to my server for validation in the header of all my AJAX requests (accompanied by the user id for database matching)
I'm mostly opening this topic to discuss best practices on how to deal with recreating hash keys, I need to figure out a way to refresh a users hash key.
Considering my experience with AJAX and JS is still rather limited, I thought about using the AJAX setup to check for a new hash, like so:
$.ajaxSetup({
beforeSend: function(xhr, settings) {
var time = new Date().getTime(); //unix time
var hash_time = localStorage.getItem("hash_expiration");
if(time>hash_time){
//ajax request to fetch new hash, async: false to make sure this completes before continuing with other AJAX calls
}
}
});
I would send the user id and his encrypted password to verify him and return a new hash.
Should I be sending AJAX requests in the ajaxSetup's beforeSend? How would this conflict with other beforeSends across my application?
Basicallly on the clients side you shouldnt have anything except hash. On the server side this hash must be associated with user it belongs to, expire time and anything else you need.
Send this hash with each request, and on server side validate it. When it expires you have to send (server) appropriate headers like 401 - Unauthorized. Client have to understand that response and try to exchange hash to new one. And finally when client gets new valid hash it can resume sending requests.
... and you shouldnt check expire time at client, this job for server.
thanks.