diff --git a/index.php b/index.php index ef041bc..ca3a832 100644 --- a/index.php +++ b/index.php @@ -1,243 +1,260 @@ - 500) { - return false; - } - return $msg; -} - -/* ======================= - ANTI PROMPT INJECTION -======================= */ -function isPromptInjection(string $msg): bool { - // regex solide : détecte toutes les tentatives d'instructions systèmes, "ignore", "révèle", "prompt", etc. - $pattern = '/\b(ignore|oublie|révèle|prompt|instructions?|system|agis comme|tu es maintenant|roleplay|administrateur)\b/i'; - return preg_match($pattern, $msg) === 1; -} - -/* ======================= - API CALL -======================= */ -function callDeepSeekAPI(string $userMessage): string { - $payload = [ - 'model' => 'deepseek-chat', - 'messages' => [ - ['role' => 'system', 'content' => SYSTEM_PROMPT], - ['role' => 'user', 'content' => $userMessage] - ], - 'max_tokens' => 800, - 'temperature' => 0, - 'top_p' => 1 - ]; - - $ch = curl_init('https://api.deepseek.com/v1/chat/completions'); - curl_setopt_array($ch, [ - CURLOPT_RETURNTRANSFER => true, - CURLOPT_POST => true, - CURLOPT_POSTFIELDS => json_encode($payload), - CURLOPT_HTTPHEADER => [ - 'Content-Type: application/json', - 'Authorization: Bearer ' . API_KEY - ], - CURLOPT_TIMEOUT => 15 - ]); - - $response = curl_exec($ch); - $http = curl_getinfo($ch, CURLINFO_HTTP_CODE); - curl_close($ch); - - if ($http !== 200 || !$response) { - return 'Erreur du service.'; - } - - $json = json_decode($response, true); - return $json['choices'][0]['message']['content'] ?? 'Erreur de réponse.'; -} - -/* ======================= - AJAX HANDLER -======================= */ -if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['message'])) { - - if (!isAjax()) { - http_response_code(403); - echo json_encode(['error' => 'Requête interdite']); - exit; - } - - if (!isset($_POST['csrf']) || $_POST['csrf'] !== $_SESSION['csrf']) { - http_response_code(403); - echo json_encode(['error' => 'CSRF invalide']); - exit; - } - - if ($_SESSION['message_count'] >= 1) { - echo json_encode(['error' => 'LIMIT_REACHED']); - exit; - } - - $message = sanitizeMessage($_POST['message']); - if ($message === false) { - echo json_encode(['error' => 'Message invalide']); - exit; - } - - // anti prompt injection - if (isPromptInjection($message)) { - echo json_encode(['response' => 'Demande à Bouchra.']); - exit; - } - - $_SESSION['message_count']++; - - $reply = callDeepSeekAPI($message); - - echo json_encode([ - 'response' => $reply - ]); - exit; -} -?> - - - - - -Assistant IA - - - - - -
-
Assistant IA
- -
-
-
- Bonjour, comment puis‑je vous aider ? -
-
-
- -
-
- Pour plus, prends l'abonnement en demandant à Bouchra -
-
- - - -
-
-
- - - - - \ No newline at end of file + 500) return false; + return $msg; +} + +/* ======================= + VALIDATION CARACTERES +======================= */ +function hasInvalidCharacters(string $msg): bool { + // Bloque TOUS les caractères spéciaux sauf ?, !, . - et ' + // Accepte : lettres, chiffres, espaces, et {?, !, ., -, '} + return preg_match('/[^a-zA-Z0-9À-ÿ\s\?\!\.\-\']/u', $msg) === 1; +} + +/* ======================= + VERBES D'ACTION INTERDITS +======================= */ +function hasBlockedVerbs(string $msg): bool { + // Bloque les verbes qui demandent une action spécifique à l'IA + $blockedVerbs = [ + 'montre', 'affiche', 'révèle', 'expose', + 'donne', 'envoie', 'fournis', 'transmets', + 'répète', 'résume', 'reformule', 'paraphrase', 'récite', + 'dis-moi', 'dis moi', 'raconte', 'explique', 'CTF', 'CTFM1' + ]; + + $msg_lower = mb_strtolower($msg); + + foreach ($blockedVerbs as $verb) { + if (preg_match('/\b' . preg_quote($verb) . '\b/i', $msg) === 1) { + return true; + } + } + + return false; +} + +/* ======================= + MOTS SENSIBLES INTERDITS +======================= */ +function hasBlockedWords(string $msg): bool { + // Bloque les mots sensibles qui demandent des infos confidentielles + $blockedWords = [ + 'flag', 'ctf', + 'token', 'secret', 'clé', 'key', + 'motdepasse', 'mot de passe', 'password', + 'prompt', 'instruction', 'système', 'system', 'règle', 'consigne' + ]; + + $msg_lower = mb_strtolower($msg); + + foreach ($blockedWords as $word) { + if (preg_match('/\b' . preg_quote($word) . '\b/i', $msg) === 1) { + return true; + } + } + + return false; +} + + +/* ======================= + RATE LIMITING +======================= */ +function checkRateLimit(): bool { + $now = time(); + $window_start = $now - RATE_WINDOW; + + $_SESSION['messages_timestamps'] = array_values( + array_filter($_SESSION['messages_timestamps'], fn($ts) => $ts > $window_start) + ); + + if (count($_SESSION['messages_timestamps']) >= RATE_LIMIT) return false; + + $_SESSION['messages_timestamps'][] = $now; + return true; +} + +/* ======================= + APPEL LLaMA via Ollama +======================= */ +function callLlamaAPI(string $userMessage): string { + // Vérifications de sécurité avec messages explicites + + if (hasInvalidCharacters($userMessage)) { + return "Je ne répondrai pas car certains caractères spéciaux sont interdits (sauf ?, !, ., - et '). Demande à Bouchra si tu veux savoir pourquoi."; + } + + if (hasBlockedVerbs($userMessage)) { + return "Je ne répondrai pas car ce message contient un verbe d'action interdit (montre, donne, répète, etc). Demande à Bouchra si tu veux savoir pourquoi."; + } + + if (hasBlockedWords($userMessage)) { + return "Je ne répondrai pas car ce message contient un mot sensible (flag, token, prompt, etc). Demande à Bouchra si tu veux savoir pourquoi."; + } + + $payload = [ + 'model' => 'llama2:7b', + 'messages' => [ + ['role'=>'system', 'content'=> SYSTEM_PROMPT], + ['role'=>'user', 'content'=> $userMessage], + ], + 'max_tokens' => 800, + 'temperature' => 0.7 + ]; + + $ch = curl_init('http://localhost:11434/v1/chat/completions'); + curl_setopt_array($ch, [ + CURLOPT_RETURNTRANSFER => true, + CURLOPT_POST => true, + CURLOPT_POSTFIELDS => json_encode($payload), + CURLOPT_HTTPHEADER => ['Content-Type: application/json'], + CURLOPT_TIMEOUT => 300 + ]); + + $response = curl_exec($ch); + $http = curl_getinfo($ch, CURLINFO_HTTP_CODE); + $error = curl_error($ch); + curl_close($ch); + + // DEBUG: Log l'erreur + error_log("Ollama API - HTTP: $http, Error: $error, Response: " . substr($response, 0, 200)); + + if ($error) { + return "Erreur de connexion à Ollama: $error"; + } + + if ($http !== 200) { + return "Erreur Ollama (HTTP $http). Vérifiez que le service tourne sur localhost:11434"; + } + + if (!$response) { + return "Pas de réponse du service LLaMA."; + } + + $json = json_decode($response, true); + + if (!$json || !isset($json['choices'][0]['message']['content'])) { + error_log("JSON decode error: " . json_last_error_msg()); + return "Erreur: réponse LLaMA malformée."; + } + + return $json['choices'][0]['message']['content']; +} + +/* ======================= + AJAX HANDLER +======================= */ +if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['message'])) { + + if (!isAjax()) { http_response_code(403); echo json_encode(['error'=>'Requête interdite']); exit; } + if (!isset($_POST['csrf']) || $_POST['csrf'] !== $_SESSION['csrf']) { http_response_code(403); echo json_encode(['error'=>'CSRF invalide']); exit; } + if (!checkRateLimit()) { http_response_code(429); echo json_encode(['error'=>'LIMIT_REACHED']); exit; } + + $message = sanitizeMessage($_POST['message']); + if ($message === false) { echo json_encode(['error'=>'Message invalide']); exit; } + + $reply = callLlamaAPI($message); + echo json_encode(['response'=>$reply]); + exit; +} + +?> + + + + + + +bobentaz + + + + + +
+
Assistant IA (bobentaz)
+ +
+
+
Bonjour, comment puis‑je vous aider ?
+
+
+ +
+
Limite de 10 messages par minute atteinte. Veuillez réessayer plus tard.
+
+ + + +
+
10 messages/minute
+
+
+ + + + +