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

Added authentication logic : XSS vuln detected #31

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
.env
test.py
40 changes: 0 additions & 40 deletions api.py

This file was deleted.

102 changes: 102 additions & 0 deletions backend/api.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
from flask import Flask, request, jsonify, redirect, url_for, session
from flask_oauthlib.client import OAuth
import requests
import ssl
import idna
import os
from dotenv import load_dotenv
from html import escape

# Load environment variables
load_dotenv()

app = Flask(__name__)
app.secret_key = os.getenv('APP_SECRET_KEY')

# OAuth Configuration
oauth = OAuth(app)
google = oauth.remote_app(
'google',
consumer_key=os.getenv('GOOGLE_CLIENT_ID'),
consumer_secret=os.getenv('GOOGLE_CLIENT_SECRET'),
request_token_params={
'scope': 'https://www.googleapis.com/auth/userinfo.email',
'prompt': 'consent'
},
base_url='https://www.googleapis.com/oauth2/v1/',
request_token_url=None,
access_token_method='POST',
access_token_url='https://accounts.google.com/o/oauth2/token',
authorize_url='https://accounts.google.com/o/oauth2/auth',
)

def check_ssl(hostname):
#TODO: Setup logic to validate SSL certificate
try:
ssl.get_server_certificate((hostname, 443))
return True
except Exception:
return False

def check_russian_chars(domain):
try:
domain.encode('idna').decode('ascii')
except idna.IDNAError:
return False
return True

@app.route('/')
def index():
return 'Welcome to PhishMeNot'

@app.route('/login')
def login():
return google.authorize(callback=url_for('authorized', _external=True))

@app.route('/login/authorized')
def authorized():
resp = google.authorized_response()
if resp is None or resp.get('access_token') is None:

# Sanitize the user input to prevent XSS attacks
error_reason = escape(request.args.get('error_reason', ''))
error_description = escape(request.args.get('error_description', ''))
return f'Access denied: reason={error_reason} error={error_description}'
Dismissed Show dismissed Hide dismissed

session['google_token'] = (resp['access_token'], '')
user_info = google.get('userinfo')

# Sanitize the user input to prevent XSS attacks in case they are rendered
user_id = escape(user_info.data['id'])
user_name = escape(user_info.data['name'])

return f'Logged in as id={user_id} name={user_name} redirecting to profile...'


@google.tokengetter
def get_google_oauth_token():
return session.get('google_token')

@app.route('/analyze', methods=['POST'])
def analyze():
if 'google_token' not in session:
return jsonify({"status": "Unauthenticated, please login first"})
data = request.json()
url = data.get('url')
hostname = data.get('hostname')

#TODO: Check URL against a database or some other logic
response = requests.get(url)

# Check SSL certificate
ssl_valid = check_ssl(hostname)

# Check for Russian characters
russian_chars_valid = check_russian_chars(hostname)

if not ssl_valid or not russian_chars_valid:
return jsonify({"status": "unsafe"})
return jsonify({"status": "safe"})

if __name__ == '__main__':
app.run(port=5000)
44 changes: 0 additions & 44 deletions chrome.js

This file was deleted.

10 changes: 5 additions & 5 deletions manifest.json
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
{
"manifest_version": 3,
"name": "PhishMeNot",
"version": "1.0",
"version": "1.0.0",
"description": "PhishMeNot allows you to surf the web and check your email safely by warning you of suspected phishing attempts.",
"permissions": ["storage", "activeTab", "webRequest"],
"host_permissions": ["<all_urls>"],
"background": {
"service_worker": "chrome.js"
"service_worker": "static/js/background.js"
},
"action": {
"default_popup": "popup.html",
"default_icon": {
"16": "images/icon_16.png",
"32": "images/icon_32.png",
"192": "images/icon_192.png"
"16": "static/images/icon_16.png",
"32": "static/images/icon_32.png",
"192": "static/images/icon_192.png"
}
}
}
Expand Down
6 changes: 5 additions & 1 deletion popup.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,12 @@
<html>
<head>
<title>PhishMeNot</title>
<link rel="stylesheet" href="static/css/style.css">
</head>
<body>
<h1>PhishMeNot Dashboard</h1>
<div class="app-root">
<button id="loginButton">Login with Google</button>
</div>
<script src="static/js/popup.js" defer="defer" charset="UTF-8"></script>
</body>
</html>
5 changes: 3 additions & 2 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
Flask==3.0.0
Flask_OAuthlib==0.9.6
idna==3.3
pyOpenSSL==23.3.0
requests==2.31.0
python-dotenv==1.0.0
requests==2.27.1
23 changes: 23 additions & 0 deletions static/css/style.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}

:root {
--popup-width: 600px;
--popup-height: 430px;
--ivory: #FEFEF4ff;
--prussian-blue: #012536ff;
--charcoal: #364954ff;
--ash-gray: #A5AFAEff;
--ash-gray-2: #B5CAC8ff;
}

html,
body {
font-family: 'Poppins', sans-serif;
background: var(--prussian-blue);
width: var(--popup-width);
height: var(--popup-height);
}
File renamed without changes
File renamed without changes
File renamed without changes
44 changes: 44 additions & 0 deletions static/js/background.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
chrome.runtime.onInstalled.addListener(function() {
initiateAuthFlow();
});

chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
if (request.action === "login") {
initiateAuthFlow(sendResponse);
return true;
}
});

function initiateAuthFlow(sendResponse) {
chrome.identity.launchWebAuthFlow(
{
url: 'http://localhost:5000/login',
interactive: true,
},
function(redirectUrl) {
if (chrome.runtime.lastError || !redirectUrl) {
console.error('Authentication failed', chrome.runtime.lastError);
sendResponse && sendResponse({status: 'failure', error: chrome.runtime.lastError});
return;
}

// Extract token from the redirect URL
const urlParams = new URLSearchParams(new URL(redirectUrl).search);
const token = urlParams.get('token'); // Assuming the token parameter name is 'token'

if (token) {
console.log('Google login successful. Token:', token);
// Store the token on chrome.storage
chrome.storage.sync.set({ 'authToken': token }, function() {
console.log('Token saved.');
});

// Notify popup or content script about successful login
sendResponse && sendResponse({status: 'success'});
} else {
console.error('No token found in redirect URL');
sendResponse && sendResponse({status: 'failure', error: 'No token found'});
}
}
);
}
11 changes: 11 additions & 0 deletions static/js/popup.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// Display version
document.addEventListener('DOMContentLoaded', function() {
const manifestData = chrome.runtime.getManifest();
const version = manifestData.version;
document.getElementById('version').textContent = `PhishMeNot V.${version}`;
});

// Login button
document.getElementById('loginButton').addEventListener('click', function() {
chrome.runtime.sendMessage({action: "login"});
});
Loading