You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于C#的Xamarin应用与PHP后端REST API安全加固咨询

Great job getting the core auth flow working! Let's lock down the security gaps you've identified—here's a step-by-step breakdown of the fixes and best practices to apply:

1. Fix the Immediate Critical Risks

Your current setup has two huge red flags that need urgent attention:

  • Plaintext password transmission via GET: Anyone sniffing network traffic can steal passwords straight from the URL.
  • Unsanitized SQL queries: Direct string concatenation makes your database wide open to SQL injection attacks.

Quick Non-Negotiable Fixes

  • Switch all auth requests to HTTPS: Without this, all other security measures are useless. Most hosting providers offer free SSL certificates via Let's Encrypt.
  • Replace GET login with POST: Mirror your signup flow and send credentials in a JSON body instead of the URL to avoid exposing sensitive data in request logs or network sniffs.

2. Password Hashing (Never Store Plaintext!)

Storing plaintext passwords is a cardinal security sin. Use PHP's built-in password_hash() and password_verify() functions—they're battle-tested, salt passwords automatically, and handle all the complex cryptography for you.

Updated Signup PHP Code

Modify your signup logic to hash passwords before storing them, and add basic client validation:

<?php
// Define your Parse-style API keys (generate random, unique values!)
define('APP_ID', 'your-custom-app-id-here');
define('REST_API_KEY', 'your-secret-rest-key-here');

// Validate Parse-style request headers first
$appId = $_SERVER['HTTP_X_PARSE_APPLICATION_ID'] ?? '';
$restKey = $_SERVER['HTTP_X_PARSE_REST_API_KEY'] ?? '';
if ($appId !== APP_ID || $restKey !== REST_API_KEY) {
    http_response_code(401);
    echo json_encode(['error' => 'Invalid client credentials']);
    exit;
}

$value = json_decode(file_get_contents('php://input'));
$mysql_pekare = new mysqli("serv", "user", "pass", "db");

if ($mysql_pekare->connect_error) {
    http_response_code(500);
    echo json_encode(['error' => 'Database connection failed']);
    exit;
}

if (!empty($value) && isset($value->Email, $value->Password)) {
    // Hash the password with PHP's secure default algorithm
    $hashedPassword = password_hash($value->Password, PASSWORD_DEFAULT);
    
    $stmt = $mysql_pekare->prepare("INSERT INTO Login (`Email`, `Password`) VALUES(?,?)");
    $stmt->bind_param("ss", $value->Email, $hashedPassword);
    
    if ($stmt->execute()) {
        $userId = $stmt->insert_id;
        echo json_encode(['UserID' => $userId]);
    } else {
        http_response_code(400);
        echo json_encode(['error' => 'Signup failed']);
    }
    
    $stmt->close();
} else {
    http_response_code(400);
    echo json_encode(['error' => 'Missing required fields']);
}

$mysql_pekare->close();
?>

3. Secure Login Flow with Session Tokens

Instead of returning any password data (even hashed), generate a secure session token after successful login. Store this token in your database and have the client use it for all subsequent authenticated requests.

Updated Login C# Code

Switch to POST, add Parse-style headers, and store the token securely using Xamarin's SecureStorage:

using Xamarin.Essentials;
using Newtonsoft.Json;

static public async Task<JObject> LoginUser(string Email, string Password) 
{ 
    var httpClientRequest = new HttpClient();
    // Add your Parse-style API headers
    httpClientRequest.DefaultRequestHeaders.Add("X-Parse-Application-Id", "your-custom-app-id-here");
    httpClientRequest.DefaultRequestHeaders.Add("X-Parse-REST-API-Key", "your-secret-rest-key-here");

    try 
    { 
        var postData = new Dictionary<string, object>
        {
            {"Email", Email},
            {"Password", Password}
        };
        var jsonRequest = JsonConvert.SerializeObject(postData);
        HttpContent content = new StringContent(jsonRequest, System.Text.Encoding.UTF8, "application/json");
        
        var result = await httpClientRequest.PostAsync("https://myURL.com/Signingup/Login.php", content);
        var resultString = await result.Content.ReadAsStringAsync();
        var jsonResult = JObject.Parse(resultString);

        // Store auth token and user ID securely if login succeeds
        if (jsonResult.ContainsKey("Token") && jsonResult.ContainsKey("UserID"))
        {
            await SecureStorage.SetAsync("AuthToken", jsonResult["Token"].ToString());
            await SecureStorage.SetAsync("UserID", jsonResult["UserID"].ToString());
        }

        return jsonResult; 
    } 
    catch 
    { 
        return null; 
    } 
}

Updated Login PHP Code

Use prepared statements to prevent SQL injection, verify password hashes, and generate a secure token:

<?php
define('APP_ID', 'your-custom-app-id-here');
define('REST_API_KEY', 'your-secret-rest-key-here');

// Validate Parse-style headers
$appId = $_SERVER['HTTP_X_PARSE_APPLICATION_ID'] ?? '';
$restKey = $_SERVER['HTTP_X_PARSE_REST_API_KEY'] ?? '';
if ($appId !== APP_ID || $restKey !== REST_API_KEY) {
    http_response_code(401);
    echo json_encode(['error' => 'Invalid client credentials']);
    exit;
}

$value = json_decode(file_get_contents('php://input'));
$connectionInfo = new ConnectionInfo();
$connectionInfo->GetConnection();

if (!$connectionInfo->conn) {
    http_response_code(500);
    echo json_encode(['error' => 'Database connection failed']);
    exit;
}

if (!empty($value) && isset($value->Email, $value->Password)) {
    // Use prepared statement to eliminate SQL injection risk
    $stmt = $connectionInfo->conn->prepare("SELECT UserID, Password FROM Login WHERE Email = ?");
    $stmt->bind_param("s", $value->Email);
    $stmt->execute();
    $result = $stmt->get_result();

    if ($user = $result->fetch_assoc()) {
        // Verify the password hash matches the provided password
        if (password_verify($value->Password, $user['Password'])) {
            // Generate a secure random token (add a `Token` column to your Login table first!)
            $token = bin2hex(random_bytes(32));
            // Store the token in the database for future requests
            $updateStmt = $connectionInfo->conn->prepare("UPDATE Login SET Token = ? WHERE UserID = ?");
            $updateStmt->bind_param("si", $token, $user['UserID']);
            $updateStmt->execute();
            $updateStmt->close();

            echo json_encode(['UserID' => $user['UserID'], 'Token' => $token]);
        } else {
            http_response_code(401);
            echo json_encode(['error' => 'Invalid email or password']);
        }
    } else {
        http_response_code(401);
        echo json_encode(['error' => 'Invalid email or password']);
    }

    $stmt->close();
} else {
    http_response_code(400);
    echo json_encode(['error' => 'Missing required fields']);
}

$connectionInfo->conn->close();

class ConnectionInfo { 
    public $conn; 
    public function GetConnection() { 
        $this->conn = mysqli_connect("server", "user","pass", "db"); 
        if (!$this->conn) {
            die("Connection failed: " . mysqli_connect_error());
        }
    } 
}
?>

4. About the Parse-Style Request Headers

Adding X-Parse-Application-Id and X-Parse-REST-API-Key is absolutely a valid security layer—it acts as a basic gatekeeper to ensure requests are coming from your legitimate app (blocking random bots or unauthorized clients from hitting your API). Here's how to implement it properly:

  • Generate unique, random values for both keys (use a secure random generator, not something guessable)
  • Hardcode these values in your Xamarin app (note: decompilation can expose them, so this is an extra layer, not a replacement for user authentication)
  • In every PHP endpoint, check that the incoming headers match your predefined keys (like we did in the code examples above)

This adds a simple barrier to entry for attackers, but remember: it doesn't authenticate individual users—you still need session tokens for that.

5. Additional Best Practices

  • Add token expiration: Add an ExpiresAt column to your Login table and invalidate tokens after a set time (e.g., 24 hours). Implement a token refresh flow for long-lived sessions.
  • Validate all input: Add server-side checks for email format, password strength, and required fields to block malformed requests.
  • Avoid exposing sensitive data: Never return database errors or hashed passwords to the client—use generic error messages instead to prevent information leakage.

内容的提问来源于stack exchange,提问作者Carlos Rodrigez

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 10:08:45