detect qr codes from URL and cid
This commit is contained in:
@@ -5,6 +5,7 @@ import com.safeqr.app.qrcode.entity.EmailEntity;
|
||||
import com.safeqr.app.qrcode.entity.QRCodeEntity;
|
||||
import com.safeqr.app.qrcode.entity.URLEntity;
|
||||
import com.safeqr.app.qrcode.service.URLVerificationService;
|
||||
import jakarta.transaction.Transactional;
|
||||
import lombok.*;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -26,7 +27,7 @@ public final class URLModel extends QRCodeModel<URLEntity> {
|
||||
this.urlVerificationService = urlVerificationService;
|
||||
details = null;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public void setDetails() {
|
||||
String url = data.getContents();
|
||||
@@ -35,11 +36,10 @@ public final class URLModel extends QRCodeModel<URLEntity> {
|
||||
urlVerificationService.countAndTrackRedirects(url, details);
|
||||
// set qrCode Identifier
|
||||
details.setQrCodeId(data.getId());
|
||||
|
||||
// Insert into URL table
|
||||
urlVerificationService.insertDB(details);
|
||||
|
||||
} catch (IOException | URISyntaxException e) {
|
||||
} catch (IOException e) {
|
||||
logger.error("Error: ", e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ import org.springframework.stereotype.Service;
|
||||
import javax.net.ssl.HttpsURLConnection;
|
||||
import java.io.IOException;
|
||||
import java.net.*;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.*;
|
||||
|
||||
|
||||
@@ -37,112 +38,150 @@ public class URLVerificationService {
|
||||
urlRepository.save(urlEntity);
|
||||
}
|
||||
// Function to breakdown URL into subdomain, domain, topLevelDomain, query params, fragment
|
||||
public URLEntity breakdownURL(String urlString) throws MalformedURLException, URISyntaxException {
|
||||
URI uri = new URI(urlString);
|
||||
URL url = uri.toURL();
|
||||
public URLEntity breakdownURL(String urlString) throws MalformedURLException {
|
||||
URLEntity urlObj = new URLEntity();
|
||||
try {
|
||||
// Ensure the URL is properly encoded
|
||||
String encodedUrl = encodeUrl(urlString);
|
||||
URI uri = new URI(encodedUrl);
|
||||
URL url = uri.toURL();
|
||||
|
||||
String host = url.getHost();
|
||||
// split host into subdomain, domain, topLevelDomain
|
||||
String[] hostParts = host.split("\\.");
|
||||
String subdomain = "";
|
||||
String host = url.getHost();
|
||||
// split host into subdomain, domain, topLevelDomain
|
||||
String[] hostParts = host.split("\\.");
|
||||
String subdomain = "";
|
||||
|
||||
if (hostParts.length >= 2) {
|
||||
// set topLevelDomain to the last part of the host
|
||||
urlObj.setTopLevelDomain(hostParts[hostParts.length - 1]);
|
||||
// set domain to the second last part of the host
|
||||
urlObj.setDomain(hostParts[hostParts.length - 2]);
|
||||
// set subdomain to the first part of the host
|
||||
if (hostParts.length > 2) {
|
||||
subdomain = String.join(".", java.util.Arrays.copyOfRange(hostParts, 0, hostParts.length - 2));
|
||||
if (hostParts.length >= 2) {
|
||||
// set topLevelDomain to the last part of the host
|
||||
urlObj.setTopLevelDomain(hostParts[hostParts.length - 1]);
|
||||
// set domain to the second last part of the host
|
||||
urlObj.setDomain(hostParts[hostParts.length - 2]);
|
||||
// set subdomain to the first part of the host
|
||||
if (hostParts.length > 2) {
|
||||
subdomain = String.join(".", java.util.Arrays.copyOfRange(hostParts, 0, hostParts.length - 2));
|
||||
}
|
||||
}
|
||||
}
|
||||
// set subdomain to URL host
|
||||
urlObj.setSubdomain(subdomain);
|
||||
// set subdomain to URL host
|
||||
urlObj.setSubdomain(subdomain);
|
||||
|
||||
String path = url.getPath();
|
||||
//set path to URL path if it's not empty, otherwise set it to root path
|
||||
urlObj.setPath(path.isEmpty() ? "/" : path);
|
||||
String path = url.getPath();
|
||||
//set path to URL path if it's not empty, otherwise set it to root path
|
||||
urlObj.setPath(path.isEmpty() ? "/" : path);
|
||||
|
||||
String query = url.getQuery();
|
||||
Map<String, String> queryParams = new HashMap<>();
|
||||
if (query != null) {
|
||||
// split query params into key value pairs
|
||||
for (String param : query.split("&")) {
|
||||
String[] pair = param.split("=");
|
||||
queryParams.put(pair[0], pair.length > 1 ? pair[1] : "");
|
||||
String query = url.getQuery();
|
||||
Map<String, String> queryParams = new HashMap<>();
|
||||
if (query != null) {
|
||||
// split query params into key value pairs
|
||||
for (String param : query.split("&")) {
|
||||
String[] pair = param.split("=");
|
||||
queryParams.put(pair[0], pair.length > 1 ? pair[1] : "");
|
||||
}
|
||||
logger.info("queryParams: {}", queryParams);
|
||||
}
|
||||
logger.info("queryParams: {}", queryParams);
|
||||
// set query params to URL query
|
||||
urlObj.setQuery(queryParams.toString());
|
||||
// set fragment to URL ref
|
||||
urlObj.setFragment(Optional.ofNullable(url.getRef()).orElse(""));
|
||||
} catch (URISyntaxException | MalformedURLException e) {
|
||||
logger.error("Error in breaking down URL: {}", e.getMessage());
|
||||
}
|
||||
// set query params to URL query
|
||||
urlObj.setQuery(queryParams.toString());
|
||||
// set fragment to URL ref
|
||||
urlObj.setFragment(Optional.ofNullable(url.getRef()).orElse(""));
|
||||
|
||||
return urlObj;
|
||||
}
|
||||
private String encodeUrl(String urlString) throws MalformedURLException {
|
||||
try {
|
||||
URL url = new URL(urlString);
|
||||
String protocol = url.getProtocol();
|
||||
String host = url.getHost();
|
||||
int port = url.getPort();
|
||||
String path = url.getPath();
|
||||
String query = url.getQuery();
|
||||
String ref = url.getRef();
|
||||
|
||||
public void countAndTrackRedirects(String urlString, URLEntity details) throws IOException, URISyntaxException {
|
||||
URI uri = new URI(urlString);
|
||||
URL url = uri.toURL();
|
||||
List<String> redirectChain = new ArrayList<>();
|
||||
List<String> hstsHeaderList = new ArrayList<>();
|
||||
List<Boolean> sslStrippingList = new ArrayList<>();
|
||||
|
||||
// Add the initial URL to the chain
|
||||
redirectChain.add(urlString);
|
||||
boolean redirected;
|
||||
int redirectCount = 0;
|
||||
|
||||
do {
|
||||
URLConnection testConnection = url.openConnection();
|
||||
|
||||
if (!(testConnection instanceof HttpURLConnection)) {
|
||||
// Handle non-HTTP connections (like mailto:)
|
||||
logger.info("Non-HTTP URL encountered: {}", url);
|
||||
hstsHeaderList.add(INFO_HSTS_NOT_APPLICABLE);
|
||||
sslStrippingList.add(false);
|
||||
break;
|
||||
StringBuilder encodedUrl = new StringBuilder();
|
||||
encodedUrl.append(protocol).append("://").append(host);
|
||||
if (port != -1) {
|
||||
encodedUrl.append(":").append(port);
|
||||
}
|
||||
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
|
||||
connection.setRequestMethod("GET");
|
||||
connection.setInstanceFollowRedirects(false);
|
||||
encodedUrl.append(URLEncoder.encode(path, StandardCharsets.UTF_8).replace("%2F", "/"));
|
||||
|
||||
int responseCode = connection.getResponseCode();
|
||||
redirected = (responseCode >= 300 && responseCode < 400);
|
||||
if (query != null) {
|
||||
encodedUrl.append("?").append(URLEncoder.encode(query, StandardCharsets.UTF_8).replace("%3D", "=").replace("%26", "&"));
|
||||
}
|
||||
if (ref != null) {
|
||||
encodedUrl.append("#").append(URLEncoder.encode(ref, StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
// Checks for HSTS Header
|
||||
hstsHeaderList.add(detectHSTSHeader(url, connection));
|
||||
return encodedUrl.toString();
|
||||
} catch (Exception e) {
|
||||
throw new MalformedURLException("Failed to encode URL: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// Handle redirects
|
||||
if (redirected) {
|
||||
// Location header contains the URL to redirect to
|
||||
String newUrl = connection.getHeaderField("Location");
|
||||
if (newUrl == null) {
|
||||
public void countAndTrackRedirects(String urlString, URLEntity details) throws IOException {
|
||||
try {
|
||||
URI uri = new URI(urlString);
|
||||
URL url = uri.toURL();
|
||||
List<String> redirectChain = new ArrayList<>();
|
||||
List<String> hstsHeaderList = new ArrayList<>();
|
||||
List<Boolean> sslStrippingList = new ArrayList<>();
|
||||
|
||||
// Add the initial URL to the chain
|
||||
redirectChain.add(urlString);
|
||||
boolean redirected;
|
||||
int redirectCount = 0;
|
||||
|
||||
do {
|
||||
URLConnection testConnection = url.openConnection();
|
||||
|
||||
if (!(testConnection instanceof HttpURLConnection)) {
|
||||
// Handle non-HTTP connections (like mailto:)
|
||||
logger.info("Non-HTTP URL encountered: {}", url);
|
||||
hstsHeaderList.add(INFO_HSTS_NOT_APPLICABLE);
|
||||
sslStrippingList.add(false);
|
||||
break;
|
||||
}
|
||||
URI newUri = uri.resolve(newUrl);
|
||||
// check for SSL stripping during redirect
|
||||
sslStrippingList.add(checkRedirectForSSLStripping(uri, newUri));
|
||||
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
|
||||
connection.setRequestMethod("GET");
|
||||
connection.setInstanceFollowRedirects(false);
|
||||
|
||||
// Handle relative URLs
|
||||
uri = uri.resolve(newUrl);
|
||||
url = uri.toURL();
|
||||
redirectChain.add(url.toString());
|
||||
redirectCount++;
|
||||
logger.info("Redirect #{}: {}",redirectCount, newUrl);
|
||||
} else {
|
||||
// No redirect, so no SSL stripping
|
||||
sslStrippingList.add(false);
|
||||
}
|
||||
int responseCode = connection.getResponseCode();
|
||||
redirected = (responseCode >= 300 && responseCode < 400);
|
||||
|
||||
connection.disconnect();
|
||||
} while (redirected && redirectCount < MAX_REDIRECT_COUNT);
|
||||
// Checks for HSTS Header
|
||||
hstsHeaderList.add(detectHSTSHeader(url, connection));
|
||||
|
||||
details.setRedirect(redirectChain.size() - 1);
|
||||
details.setRedirectChain(redirectChain);
|
||||
details.setSslStripping(sslStrippingList);
|
||||
details.setHstsHeader(hstsHeaderList);
|
||||
// Handle redirects
|
||||
if (redirected) {
|
||||
// Location header contains the URL to redirect to
|
||||
String newUrl = connection.getHeaderField("Location");
|
||||
if (newUrl == null) {
|
||||
break;
|
||||
}
|
||||
URI newUri = uri.resolve(newUrl);
|
||||
// check for SSL stripping during redirect
|
||||
sslStrippingList.add(checkRedirectForSSLStripping(uri, newUri));
|
||||
|
||||
// Handle relative URLs
|
||||
uri = uri.resolve(newUrl);
|
||||
url = uri.toURL();
|
||||
redirectChain.add(url.toString());
|
||||
redirectCount++;
|
||||
logger.info("Redirect #{}: {}",redirectCount, newUrl);
|
||||
} else {
|
||||
// No redirect, so no SSL stripping
|
||||
sslStrippingList.add(false);
|
||||
}
|
||||
|
||||
connection.disconnect();
|
||||
} while (redirected && redirectCount < MAX_REDIRECT_COUNT);
|
||||
|
||||
details.setRedirect(redirectChain.size() - 1);
|
||||
details.setRedirectChain(redirectChain);
|
||||
details.setSslStripping(sslStrippingList);
|
||||
details.setHstsHeader(hstsHeaderList);
|
||||
} catch (URISyntaxException e){
|
||||
logger.error("Error in breaking down URL: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
// Function to check if the redirect is from HTTPS to HTTP
|
||||
private boolean checkRedirectForSSLStripping(URI originalUri, URI newUri) {
|
||||
|
||||
Reference in New Issue
Block a user