Skip to content

Commit 9a8754a

Browse files
author
HugoFara
committed
fix(inc): validateLang had unsafe type use.
1 parent 4cfda6a commit 9a8754a

File tree

3 files changed

+90
-26
lines changed

3 files changed

+90
-26
lines changed

docs/CHANGELOG.md

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,13 @@ ones are marked like "v1.0.0-fork".
1111

1212
* Official support for PHP 8.3 and 8.4.
1313

14+
### Security
15+
16+
* Fixed SQL injection vulnerabilities in `validateLang()`, `validateText()`,
17+
`validateTag()`, and `validateArchTextTag()` functions. All validation
18+
functions now properly sanitize non-numeric input before executing SQL
19+
queries.
20+
1421
### Deprecated
1522

1623
* Removed testing for PHP 8.0.
@@ -661,7 +668,7 @@ from 1.8.0 to 1.10.2. This brings some bug fixes.
661668
### Fixed in 2.4.1-fork
662669

663670
* A typo was breaking the feeds count in ``edit_languages.php``, creating annoying notices as illustrated at [#35](https://github.com/HugoFara/lwt/issues/35).
664-
* The error "[1290] The MySQL server is running with the --secure-file-priv option" should no longer appear when trying to save Japanese texts. It was referenced [here](https://github.com/HugoFara/lwt/issues/34#issuecomment-1141976723) in [issue #34](https://github.com/HugoFara/lwt/issues/34).
671+
* The error "[1290] The MySQL server is running with the --secure-file-priv option" should no longer appear when trying to save Japanese texts. It was referenced [in this comment](https://github.com/HugoFara/lwt/issues/34#issuecomment-1141976723) in [issue #34](https://github.com/HugoFara/lwt/issues/34).
665672
* The "Undefined index: trans in .../bulk_translate_word.php" notice fixed.
666673
* The "Undefined index: WoText in .../delete_word.php" notice fixed.
667674
* Repaired ``long_text_import.php`` for non-Japanese texts, it was broken since 2.4.0. Thanks to [@rc-ops](https://github.com/rc-ops) for this issue [#33](https://github.com/HugoFara/lwt/issues/33).
@@ -999,7 +1006,7 @@ This version should be the stable merge between official v2.0.2 and community ma
9991006

10001007
### Added in 1.6.1
10011008

1002-
* [Link](info.html#links) to Chinese text segmentation "Jieba" added in documentation (Important Links - Additional Resources - For learners of Chinese).
1009+
* [Chinese text segmentation "Jieba" link](info.html#links) added in documentation (Important Links - Additional Resources - For learners of Chinese).
10031010

10041011
### Changed in 1.6.1
10051012

@@ -1566,8 +1573,8 @@ Changes from official LWT version 1.5.20 imported:
15661573

15671574
### Added in 1.5.5
15681575

1569-
* Integration of the Glosbe API into LWT via a "special" dictionary link. Read more [here](info.html#glosbe).
1570-
* LWT-WordPress integration, read more [here](info.html#wp) (only for users who want to use WordPress authentication together with the LWT multiple user/table set feature introduced in version 1.5.3).
1576+
* Integration of the Glosbe API into LWT via a "special" dictionary link. Read more [in the Glosbe documentation](info.html#glosbe).
1577+
* LWT-WordPress integration, read more [in the WordPress integration guide](info.html#wp) (only for users who want to use WordPress authentication together with the LWT multiple user/table set feature introduced in version 1.5.3).
15711578

15721579
### Changed in 1.5.5
15731580

@@ -1590,7 +1597,7 @@ Changes from official LWT version 1.5.20 imported:
15901597

15911598
### Added in 1.5.3
15921599

1593-
* New Feature: It is now possible to create and to use not only ONE set of LWT tables within one database. You are now able to create and use unlimited LWT table sets within one database (as space and MySQL limitations permit). This feature is especially useful for users who want to set up a multi user environment with a set of tables for each user. You can also create one table set for every language you study - this allows you to create different term/text tags for each language. If you don't need this feature, you just use LWT like in earlier versions with the "default table set". Read more [here](info.html#mue) and [here](info.html#database).
1600+
* New Feature: It is now possible to create and to use not only ONE set of LWT tables within one database. You are now able to create and use unlimited LWT table sets within one database (as space and MySQL limitations permit). This feature is especially useful for users who want to set up a multi user environment with a set of tables for each user. You can also create one table set for every language you study - this allows you to create different term/text tags for each language. If you don't need this feature, you just use LWT like in earlier versions with the "default table set". Read more [in the multi-user environment guide](info.html#mue) and [database documentation](info.html#database).
15941601

15951602
### Changed in 1.5.3
15961603

@@ -1622,7 +1629,7 @@ Changes from official LWT version 1.5.20 imported:
16221629

16231630
### Added in 1.5.0
16241631

1625-
* New Feature: Create and edit an improved annotated text version (as [interlinear text](http://en.wikipedia.org/wiki/Interlinear_gloss)) for online or offline learning. Read more [here](info.html#il).
1632+
* New Feature: Create and edit an improved annotated text version (as [interlinear text](http://en.wikipedia.org/wiki/Interlinear_gloss)) for online or offline learning. Read more [in the interlinear text documentation](info.html#il).
16261633
* In-Place-editing of translations and romanizations now possible within the terms table.
16271634
* You may now empty (= delete the contents of) the LWT database in the "Backup/Restore/Empty Database" screen.
16281635

@@ -1841,7 +1848,7 @@ Changes from official LWT version 1.5.20 imported:
18411848

18421849
### Added in 1.0.2
18431850

1844-
* Language definition: If the searchword in the Uniform Resource Identifiers (URIs) needs to be converted into a different encoding (standard is UTF-8), you can now use *###encoding###* as a placeholder. Example: *<http://mywebdict.com?q=###ISO-8859-15###>*. A list of encodings can be found [here](http://php.net/manual/en/mbstring.supported-encodings.php) (omit the asterisk if one is at the end).
1851+
* Language definition: If the searchword in the Uniform Resource Identifiers (URIs) needs to be converted into a different encoding (standard is UTF-8), you can now use *###encoding###* as a placeholder. Example: *<http://mywebdict.com?q=###ISO-8859-15###>*. A list of encodings can be found [in the PHP mbstring documentation](http://php.net/manual/en/mbstring.supported-encodings.php) (omit the asterisk if one is at the end).
18451852

18461853
### Changed in 1.0.2
18471854

@@ -1860,7 +1867,7 @@ Changes from official LWT version 1.5.20 imported:
18601867

18611868
## 1.0.0 (August 01 2011)
18621869

1863-
* First stable release. For some time, there won't be any new releases. I hope you'll understand that. Please post all problems, questions, and (hopefully not too many) bugs [here](http://sourceforge.net/projects/lwt/forums/forum/1813497), and ideas and suggestions for new features [here](http://lwt.uservoice.com). Thanks!
1870+
* First stable release. For some time, there won't be any new releases. I hope you'll understand that. Please post all problems, questions, and (hopefully not too many) bugs [on the SourceForge forum](http://sourceforge.net/projects/lwt/forums/forum/1813497), and ideas and suggestions for new features [on UserVoice](http://lwt.uservoice.com). Thanks!
18641871

18651872
## 0.9.8 (July 31 2011)
18661873

inc/database_connect.php

Lines changed: 41 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -202,40 +202,44 @@ function ($a) {
202202
function validateLang($currentlang): string
203203
{
204204
global $tbpref;
205-
if ($currentlang == '') {
205+
if ($currentlang == '' || !is_numeric($currentlang)) {
206206
return '';
207207
}
208+
// Cast to integer for safety against SQL injection
209+
$currentlang_int = (int)$currentlang;
208210
$sql_string = 'SELECT count(LgID) AS value
209211
FROM ' . $tbpref . 'languages
210-
WHERE LgID=' . $currentlang;
212+
WHERE LgID=' . $currentlang_int;
211213
if (get_first_value($sql_string) == 0) {
212214
return '';
213215
}
214-
return $currentlang;
216+
return (string)$currentlang_int;
215217
}
216218

217219
/**
218220
* Validate a text ID
219221
*
220222
* @param string $currenttext Text ID to validate
221223
*
222-
* @global string '' if the text is not valid, $currenttext otherwise
224+
* @return string '' if the text is not valid, $currenttext otherwise
223225
*
224226
* @global string $tbpref Table name prefix
225227
*/
226228
function validateText($currenttext): string
227229
{
228230
global $tbpref;
229-
if ($currenttext == '') {
231+
if ($currenttext == '' || !is_numeric($currenttext)) {
230232
return '';
231233
}
234+
// Cast to integer for safety against SQL injection
235+
$currenttext_int = (int)$currenttext;
232236
$sql_string = 'SELECT count(TxID) AS value
233237
FROM ' . $tbpref . 'texts WHERE TxID=' .
234-
$currenttext;
238+
$currenttext_int;
235239
if (get_first_value($sql_string) == 0) {
236240
return '';
237241
}
238-
return $currenttext;
242+
return (string)$currenttext_int;
239243
}
240244

241245
// -------------------------------------------------------------
@@ -244,12 +248,27 @@ function validateTag($currenttag,$currentlang)
244248
{
245249
global $tbpref;
246250
if ($currenttag != '' && $currenttag != -1) {
251+
// Sanitize inputs to prevent SQL injection
252+
if (!is_numeric($currenttag)) {
253+
return '';
254+
}
255+
$currenttag_int = (int)$currenttag;
256+
257+
$lang_condition = '';
258+
if ($currentlang != '') {
259+
if (!is_numeric($currentlang)) {
260+
return '';
261+
}
262+
$currentlang_int = (int)$currentlang;
263+
$lang_condition = " AND WoLgID = " . $currentlang_int;
264+
}
265+
247266
$sql = "SELECT (
248-
" . $currenttag . " IN (
267+
" . $currenttag_int . " IN (
249268
SELECT TgID
250269
FROM {$tbpref}words, {$tbpref}tags, {$tbpref}wordtags
251270
WHERE TgID = WtTgID AND WtWoID = WoID" .
252-
($currentlang != '' ? " AND WoLgID = " . $currentlang : '') .
271+
$lang_condition .
253272
" group by TgID order by TgText
254273
)
255274
) AS value";
@@ -289,9 +308,15 @@ function validateArchTextTag($currenttag,$currentlang)
289308
{
290309
global $tbpref;
291310
if ($currenttag != '' && $currenttag != -1) {
311+
// Sanitize inputs to prevent SQL injection
312+
if (!is_numeric($currenttag)) {
313+
return '';
314+
}
315+
$currenttag_int = (int)$currenttag;
316+
292317
if ($currentlang == '') {
293318
$sql = "select (
294-
" . $currenttag . " in (
319+
" . $currenttag_int . " in (
295320
select T2ID
296321
from {$tbpref}archivedtexts,
297322
{$tbpref}tags2,
@@ -302,13 +327,17 @@ function validateArchTextTag($currenttag,$currentlang)
302327
) as value";
303328
}
304329
else {
330+
if (!is_numeric($currentlang)) {
331+
return '';
332+
}
333+
$currentlang_int = (int)$currentlang;
305334
$sql = "select (
306-
" . $currenttag . " in (
335+
" . $currenttag_int . " in (
307336
select T2ID
308337
from {$tbpref}archivedtexts,
309338
{$tbpref}tags2,
310339
{$tbpref}archtexttags
311-
where T2ID = AgT2ID and AgAtID = AtID and AtLgID = $currentlang
340+
where T2ID = AgT2ID and AgAtID = AtID and AtLgID = " . $currentlang_int . "
312341
group by T2ID order by T2Text
313342
)
314343
) as value";

tests/inc/DatabaseConnectTest.php

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -223,12 +223,26 @@ public function testValidateLang(): void
223223
$this->assertEquals('', $result);
224224

225225
// Test with a language that doesn't exist (should return empty)
226-
// Using a numeric value to avoid SQL errors
227226
$result = validateLang('99999');
228227
$this->assertEquals('', $result);
229228

230-
// Note: Non-numeric values cause SQL errors in the current implementation
231-
// This is a potential bug that should be fixed by sanitizing input before SQL
229+
// Test SQL injection attempts - these should be safely rejected
230+
$result = validateLang("1 OR 1=1");
231+
$this->assertEquals('', $result, 'SQL injection attempt should be rejected');
232+
233+
$result = validateLang("invalid");
234+
$this->assertEquals('', $result, 'Non-numeric input should be rejected');
235+
236+
$result = validateLang("1; DROP TABLE languages; --");
237+
$this->assertEquals('', $result, 'SQL injection with DROP TABLE should be rejected');
238+
239+
$result = validateLang("1' OR '1'='1");
240+
$this->assertEquals('', $result, 'SQL injection with quotes should be rejected');
241+
242+
// Valid numeric strings should work (if language exists)
243+
$result = validateLang('1');
244+
// Result depends on if language ID 1 exists, but shouldn't crash
245+
$this->assertTrue($result === '' || $result === '1', 'Valid numeric ID should return empty or the ID');
232246
}
233247

234248
/**
@@ -251,12 +265,26 @@ public function testValidateText(): void
251265
$this->assertEquals('', $result);
252266

253267
// Test with a text that doesn't exist (should return empty)
254-
// Using a numeric value to avoid SQL errors
255268
$result = validateText('99999');
256269
$this->assertEquals('', $result);
257270

258-
// Note: Non-numeric values cause SQL errors in the current implementation
259-
// This is a potential bug that should be fixed by sanitizing input before SQL
271+
// Test SQL injection attempts - these should be safely rejected
272+
$result = validateText("1 OR 1=1");
273+
$this->assertEquals('', $result, 'SQL injection attempt should be rejected');
274+
275+
$result = validateText("invalid");
276+
$this->assertEquals('', $result, 'Non-numeric input should be rejected');
277+
278+
$result = validateText("1; DROP TABLE texts; --");
279+
$this->assertEquals('', $result, 'SQL injection with DROP TABLE should be rejected');
280+
281+
$result = validateText("1' UNION SELECT * FROM users --");
282+
$this->assertEquals('', $result, 'SQL injection with UNION should be rejected');
283+
284+
// Valid numeric strings should work (if text exists)
285+
$result = validateText('1');
286+
// Result depends on if text ID 1 exists, but shouldn't crash
287+
$this->assertTrue($result === '' || $result === '1', 'Valid numeric ID should return empty or the ID');
260288
}
261289

262290
/**

0 commit comments

Comments
 (0)